Files
runi-dist/docs/user-flows.md
T

20 KiB

User flows

See config-schema.md for the exact fields referenced below, and architecture.md (source repository) for how run resolves all of this internally.

1. First run on a fresh machine

runi versions auto-discovers whatever's already installed (Steam's official Proton if Steam is present; nothing else yet). To get GE-Proton: runi versions list-remote --source ge-proton shows installable releases, runi versions install ge-proton latest downloads, verifies checksum, extracts into compatibilitytools.d with a progress bar, and now shows up in runi versions.

2. Add and run a standalone .exe (no storefront)

runi add ~/Games/MyIndieGame/game.exe --name "My Indie Game"
runi run my-indie-game

Exe path validated, game.id = my-indie-game (unique slug), identity.umu_gameid = umu-default (shared fallback, no game-specific fixes — expected for an unknown title). run resolves backend=umu (the global default), builds PROTONPATH/WINEPREFIX/GAMEID=umu-default, execs umu-run. First launch creates the Wine prefix.

3. Add a second build of a game you already have

Keeping two builds installed at once — a working one and a newer one you want to test — is a normal thing to do. Both derive the same id, so Runi resolves it instead of overwriting:

runi add ~/Games/Mortal.Shell.II.v92120-P2P/.../MortalShell2-Win64-Shipping.exe \
  --name "Mortal Shell II"

added mortal-shell-ii-cl92120 (Mortal Shell II)
note: a different copy of 'Mortal Shell II' is already registered as
mortal-shell-ii; this one is registered alongside it (build version cl92120,
from pe-file-version)
shared with the other copy: game identity, notes, artwork
kept separate: Wine prefix, settings, environment, logs, save backups, mod receipts

The suffix is the build version read from the executable, so the id says which copy it is. When the two copies report the same version — common for engines that stamp their own version rather than the game's — there is nothing to tell them apart and Runi asks instead of guessing:

Error: 'Ascend to ZERO' is already registered as ascend-to-zero and no build
version tells the two copies apart; re-run with --id <unique-id> to add it as a
separate installation

In the GUI this is one dialog with the proposed id pre-filled and editable, showing the raw version string it came from.

If you moved or renamed the folder instead, that is not a second copy — Runi recognises the installation by the marker inside it and offers to repoint the existing registration, keeping its prefix, saves, settings and mods:

Error: this installation is already registered as 'Mortal Shell II'
(mortal-shell-ii); it appears to have moved

4. Add a Steam game with a different Proton version than Steam's own choice

runi steam-scan
runi add --steam-appid 292030 --backend native --proton GE-Proton9-27

steam-scan lists installed titles with their AppIDs. add --steam-appid resolves title/exe path automatically from the scan (no manual path-typing), sets game.id = steam-292030, identity.steam_appid = 292030, and identity.umu_gameid = umu-292030 (so protonfixes still key correctly even though this game runs outside Steam's own launch path). runi versions would have already flagged GE-Proton9-27 as safe for native (no ⚠ needs steamrt), so add doesn't need to warn here.

In the GUI, Runtime separates Launch Engine from Runner family and Version. UMU Automatic selects the newest installed GE-Proton; Direct Proton selects an explicit installed build.

Manual game creation asks for the executable and suggests Display Name from a local Steam manifest, PE ProductName/FileDescription, a cleaned installation folder or finally the executable filename. The player may edit the suggestion. Runi then resolves Steam/UMU identity in the background from a game-provided steam_appid.txt, corroborated AppID metadata in bounded local INI files, an exact local Steam manifest match, or an exact Steam Store title match. Local AppIDs are validated directly through Steam app details, so an imperfect folder title does not prevent identification. Ambiguous matches require confirmation; unavailable or failed validation leaves umu-default. Runtime shows the canonical title, source, confidence and validation timestamp, retains editable technical fields and offers explicit revalidation. Settings → Runners manages GE-Proton, UMU-Proton, Proton-CachyOS and experimental Proton-EM releases. Proton Experimental is detected but remains Steam-managed. Installed versions show manager and usage badges. Selecting a games badge expands the titles configured to use that exact version; removal remains disabled until those games are moved to another Runtime version. Selecting an already installed release changes the action to Installed. New builds are installed transactionally, so Runi never extracts over an existing runner or exposes a partially extracted build to Steam/UMU.

Add from Dolphin

On KDE Plasma, enable Settings → Desktop Integration → Show “Add to Runi” in Dolphin. Right-click one local Windows .exe and choose Add to Runi. Dolphin sends the path to the already-running GUI, or starts it when necessary, and the normal Add Game review opens.

The local candidate check is shared by Dolphin, the GUI and runi add. Malformed files and DLLs are blocked. A valid PE whose role is unclear, or a file that resembles a settings/installer utility, is shown with evidence and ranked alternatives but may still be added deliberately. Selecting a game already in the library focuses its existing card instead of creating a duplicate. Requests received while a game or modal task is active remain in a small in-memory queue.

The same entry point is available for desktop integration testing:

runi gui --add-file /path/to/game.exe
runi desktop-integration dolphin status
runi desktop-integration dolphin install
runi desktop-integration dolphin remove

5. Add a GOG/Epic title

runi add ~/Games/SomeGogGame/game.exe --store gog --name "Some Game"

Manual registration in v1 (no Heroic/Legendary auto-scan yet — see roadmap.md (source repository)); STORE=gog is still forwarded so protonfixes keyed by store apply if the umu-database has an entry for it.

6. Add games from Ubisoft Connect

In the GUI choose + → Ubisoft Connect. Install the client automatically or import an existing Ubisoft prefix, open it to log in/install games, then close it. Review the locally detected titles and import the selected ones. Later, Play sends the Ubisoft deeplink directly; Tools → Stores reopens client management. See ubisoft-connect.md for the shared-prefix, runner, process-monitoring and cloud-sync rules.

7. Tweak display settings on a stuttery game

runi config steam-292030 \
  --set gamescope.enabled=true \
  --set gamescope.output_width=2560 --set gamescope.output_height=1440 \
  --set gamescope.render_width=1920 --set gamescope.render_height=1080 \
  --set gamescope.filter=fsr \
  --set mangohud.enabled=true

runi run steam-292030 --dry-run

--dry-run prints the fully composed command so you can verify before committing:

gamescope -W 2560 -H 1440 -w 1920 -h 1080 -F fsr --mangoapp -- umu-run witcher3.exe

Note MangoHud folded into gamescope's own --mangoapp flag rather than becoming a separate wrapper stage (gamescope's nested compositor doesn't support the plain MangoHud overlay). Drop --dry-run to launch for real.

8. Install OptiScaler for a game with a weak native upscaler

runi optiscaler install steam-292030 --dll dxgi.dll
runi run steam-292030

Downloads the release, backs up any dxgi.dll the game already shipped, copies OptiScaler's files alongside the game exe, sets env.WINEDLLOVERRIDES = "dxgi=n,b" and [optiscaler] state in the config. run launches normally; in-game Insert opens the OptiScaler overlay to pick an upscaler backend.

runi optiscaler remove steam-292030

Restores the original file from the recorded manifest and clears the env override.

9. Manage Proton versions over time

runi versions install ge-proton latest
runi versions remove GE-Proton9-20

The second command fails with VersionInUseError listing which games still reference that version; re-point those games first (runi config <id> --set runtime.proton=GE-Proton9-27), then removal succeeds.

10. Clean up

runi remove my-indie-game

The CLI deletes the registration and its Wine prefix by default; pass --keep-prefix to preserve the prefix. In the GUI, removing only the registration is the safe default. A separate checkbox can permanently delete the marked game installation and its prefix, and warns that local saves and settings stored in either location are also deleted. Runi refuses this disk deletion when the installation ownership marker does not match. The originally selected folder remains the deletion unit even when the executable is nested deeply; recognized Unreal packages include the sibling Engine directory rather than only Binaries/Win64. Existing Runi save backups are kept.

For Ubisoft games, the shared prefix is always preserved. The GUI can open Ubisoft Connect's own uninstall confirmation before removing the Runi registration; it never deletes the Ubisoft game directory itself.

11. Recipes, and what replaced them

There is no Recipe tab, and adding a game no longer offers one. Recipes asked the player to assemble a setup from required items, recommended items, supplied asset files and a review step - which is a great deal of deciding for someone who wanted to press Play. The OptiScaler tab now narrows the same ground to two explicit choices with automatic selection between them, so the flow that used to live here is flow 8.

What remains is rollback. A game that had a recipe applied before the retirement still carries its receipt, and shows a Recipe (Legacy) entry for as long as that receipt exists, so the transaction can still be reverted. Nothing creates a new one.

For an upscaler build with no official source to automate - a leaked FSR package, a DLSS/XeSS swap - the supported path is a customize profile, which is flow 13.

12. Known launch compatibility

When Runi recognises an exact canonical game with a verified launch workaround, it applies the safe default during registration or before launch and shows a short notification. Explicit user launch options take precedence.

The first verified case is Pathogenic's Windows build on Linux: its Godot 4.7 D3D12 path crashed in RADV while creating a VKD3D compute pipeline, while the same executable ran correctly with --rendering-driver vulkan. This argument is scoped to Pathogenic's PGID; it is not inferred for other Godot games.

13. Apply a community upscaler workaround manually (advanced)

Real example, validated end-to-end: enabling a leaked FSR 4.1.1 INT8 build on non-RDNA4 hardware, for a game with OptiScaler already installed.

runi optiscaler install my-game --dll dxgi.dll

Write ~/.config/runi/profiles/fsr411-workaround.toml once (see customize-profiles.md for the full format):

[profile]
name = "fsr411-workaround"
description = "Community FSR 4.1.1 INT8 workaround for non-RDNA4 GPUs"

[[files]]
src = "/home/user/Downloads/FidelityFX-Samples-v2.3.0-prebuilt/.../amd_fidelityfx_upscaler_dx12.dll"
dest = "amd_fidelityfx_upscaler_dx12.dll"

[[ini_edits]]
file = "OptiScaler.ini"
[ini_edits.set]
Fsr4Update = "true"
runi customize apply my-game fsr411-workaround
runi run my-game

In-game, press Insert for the OptiScaler overlay and select FSR4 → the INT8 model. If a newer leaked build shows up later, just edit the profile's [[files]] source paths and re-apply — the previous version is reverted automatically before the new one is applied.

runi customize remove my-game

Restores the pre-customize state exactly (the stock OptiScaler files, Fsr4Update back to its original value) — OptiScaler itself stays installed.

14. Enable LinUwUx for a Reflex/DenuvOwO-protected game

runi config my-game --set linuwux.enabled=true
runi run my-game --dry-run

--dry-run confirms the wrapper is in place:

linuwux umu-run MyGame.exe

Drop --dry-run to launch for real. If the Technology Scan (GUI Overview tab, or runi doctor) already flagged a "Compat Shim: LinUwUx (Reflex)" or "LinUwUx (DenuvOwO)" finding for this game, that's an informational hint only — Runi never enables the wrapper automatically.

[linuwux].debug=true adds LINUWUX_DEBUG=1 for troubleshooting; leave it off for normal play, since it produces heavy sigsys redirect logging.

[linuwux].enabled cannot be combined with [gamescope].enabled — confirmed crashing on real hardware (the gamescope session dies before the game process ever starts), not just untested. runi run/--dry-run refuse with IncompatibleWrapperError up front rather than launching into a crash; disable gamescope first if you need linuwux.

[mangohud].enabled works fine alongside linuwux — Runi activates it with MANGOHUD=1 alone instead of mangohud's usual LD_PRELOAD wrapper (which does collide), confirmed working on real hardware. No extra configuration needed; just enable both.

If you want a custom MangoHud layout via [mangohud].config_file, MangoHud's own core_load option (per-core CPU load/frequency) is worth knowing about: bisected on real hardware (Monster Hunter Wilds) that enabling core_load forces MangoHud back to a vertical layout even with horizontal set - a per-core breakdown doesn't fit a horizontal row. Every other stat option (cpu_stats, gpu_stats, ram, vram, frame_timing, etc.) composes fine with horizontal; only core_load is the exception. This is MangoHud's own behavior, unrelated to Runi or linuwux specifically.

That exact layout ships as a bundled preset - see flow 15 below - so you don't have to rebuild it by hand: runi mangohud-preset apply horizontal-complete <id>.

LinUwUx upstream documents only GE-Proton and Proton-CachyOS as supported (official Valve Proton is not). If [runtime].proton resolves to anything else, runi doctor surfaces a non-blocking advisory finding rather than refusing to launch:

runi doctor

Requires linuwux to already be on PATH (installed via upstream's own install.sh/build.sh --install — see github.com/brcly/linuwux-runtime); Runi never installs or manages it.

15. Save and apply a named MangoHud preset

runi mangohud-preset list

Shows every available preset — bundled (shipped with Runi) plus any you've saved locally. Runi ships one bundled preset out of the box, horizontal-complete, the exact layout validated in flow 14 above:

runi mangohud-preset apply horizontal-complete my-game
runi config my-game --set mangohud.enabled=true

This writes the preset's content to a per-game file (~/.local/share/runi/mangohud/<id>.conf) and sets [mangohud].config_file to it — the same effect as building the file by hand and pointing config_file at it, just one command. runi run my-game --dry-run shows the resolved MANGOHUD_CONFIGFILE to confirm.

To save your own config (built by hand, or already applied to a game you're happy with) as a reusable preset:

runi mangohud-preset save my-layout --from-game my-game --description "my favorite layout"
# or, from an arbitrary file:
runi mangohud-preset save my-layout --from-file ~/some/MangoHud.conf

Saved presets are plain TOML at ~/.config/runi/mangohud-presets/<name>.toml; a local preset with the same name as a bundled one takes priority, so you can override horizontal-complete itself by saving your own version under that name.

runi mangohud-preset remove my-game

Clears [mangohud].config_file (falling back to MangoHud's own default resolution order) and deletes the applied per-game file.

Deliberately per-game only — there is no "apply globally" (writing ~/.config/MangoHud/MangoHud.conf). That file is commonly managed by third-party tools like GOverlay/MangoJuice; a second tool silently writing over it (even with a backup taken first) creates an ongoing "who owns this file" conflict with no clean resolution, confirmed real on a machine already running MangoJuice. If you want a preset's layout system-wide, apply it to each game you care about, or use GOverlay/MangoJuice's own global-config tooling directly.

16. Import a Ludusavi save backup into a game's prefix

CLI-only, low-key utility — reads a Ludusavi backup folder (mapping.yaml + drive-C/, the "simple" non-zip format) directly and copies the files into the game's own Wine prefix. No dependency on the ludusavi binary itself.

runi saves import-ludusavi my-game /path/to/ludusavi/backups/My\ Game --preview

--preview shows exactly what would be copied — source → destination → size — without writing anything. The prefix must already exist (launch the game for real at least once first); otherwise this refuses rather than half-creating one.

runi saves import-ludusavi my-game /path/to/ludusavi/backups/My\ Game

Copies every recorded file, verifying each one's SHA1 against what mapping.yaml recorded (catches SMB/network corruption, not just logic bugs), and — unless --no-set-save-path is passed — updates [saves].path to the backup's own save location, so Runi's own saves backup/restore also point at the right place afterward. If the computed destination already has files in it, the current state is backed up first via the normal saves backup mechanism, so an import is always reversible.

Two real things this surfaced, worth knowing if a result looks surprising:

  • Path casing and shared folders. Ludusavi records the source Windows machine's own path casing (Users, capitalized) and username segment (e.g. Users/cvarandas/...). Wine's real prefix directory is lowercase (users) with its own account name (steamuser in every runi-managed prefix) — a blind copy would create a bogus sibling directory the game never sees. The importer resolves both against what's actually on disk. It also knows Users/Public/... is a real, shared folder, not a personal account to remap — confirmed by a real game (The Sinking City 2) whose save data legitimately lives partly under Public.
  • A RUNE/crack config's declared save path and a Ludusavi backup's actual files can legitimately disagree. The Sinking City 2's RUNE crack (steam_emu.ini) declares saves at users/Public/Documents/Steam/RUNE/2825860 — that's likely where RUNE stores its own Steam-emulation state (fake achievements/cloud markers), not necessarily the engine's native save files, which for this game turned out to be AppData/Local/TSC2/Saved (confirmed by both the real backup content and an actual game launch). The importer prints a note when this happens (reusing Runi's existing RUNE detection as a cross-check) but always imports to where the backup's files really are.

Runi cannot verify a backup matches the installed game's specific version/release — Ludusavi's mapping.yaml records hashes for the save data itself, not the source machine's game build. The safety backup above is the actual mitigation; confirm compatibility the same way it was confirmed here — launch the game and check.

Only the simple (non-zip) backup format is supported; a zip-format backup folder is refused with a clear error rather than silently mishandled.