-
-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture 08 Library Local
This page documents the three modules that build Aurelia's view of a user's Steam games and the connection endpoints used to reach Steam:
| Module | File | Responsibility |
|---|---|---|
| Library abstraction | src/library.rs |
Merge the owned (account-licensed) library with what is installed on disk, produce the data behind aurelia list, and filter Steam tooling. |
| Local install scanner | src/local_library.rs |
Discover owned games from the Steam client's own on-disk caches when Aurelia has no network session. |
| CM server list | src/cm_list.rs |
Resolve the list of Steam Connection Manager (CM) endpoints to dial for the binary Steam protocol. |
Note on naming:
library.rscontains both the higher-level merge/filter logic and the on-disk appmanifest/libraryfolders.vdfscanner.local_library.rs, despite the name, is the owned-library-from-local-Steam-caches fallback, not the installed-game scanner. See §3.
There are two distinct "sources of truth" for a Steam library, and Aurelia reconciles them:
-
Owned library (account licenses). The full set of games a Steam account is
licensed to play. Normally fetched over the network (
fetch_owned_games, requiresaurelia login). When there is no session, it is reconstructed from the local Steam client caches bylocal_library.rs. -
Installed games (bytes on disk). What is actually downloaded into a Steam
library folder, discovered by scanning
appmanifest_*.acffiles. This lives inlibrary.rs(scan_*functions).
library.rs::build_game_library is the join point: it takes an owned list plus an
installed map and emits a single GameLibrary of LibraryGame rows that drive
aurelia list. Tooling (Proton, runtimes, redistributables) is filtered out of both
sides.
network/cache owned games ─┐
├─► build_game_library ─► GameLibrary { Vec<LibraryGame> }
on-disk appmanifest scan ──┘ (src/library.rs:421)
Steam installs runtimes/redistributables/Proton as "apps" that should never appear as launchable titles. They are hidden two ways:
| Mechanism | Definition | Used by |
|---|---|---|
| App-id deny-list |
IGNORED_STEAM_APP_IDS: src/library.rs:13 (e.g. 228980 redistributables, 1493710 Proton Experimental, Steam Linux Runtime ids) |
is_ignored_steam_app |
| Name-prefix deny-list |
IGNORED_STEAM_APP_NAME_PREFIXES: src/library.rs:26 ("Steam Linux Runtime", "Proton", "Steamworks Common Redistributables") |
is_ignored_steam_app |
is_ignored_steam_app(app_id, name) (src/library.rs:34) returns true if either the
id is in the deny-list or the (left-trimmed) name starts with a deny prefix. It mirrors
Heroic's ignoredSteamAppIds/ignoredSteamAppNamePrefixes. Tests at
src/library.rs:544-587 confirm "The Protonist" (a real game) is not filtered.
build_game_library(owned, installed_info, steam_id) → GameLibrary
(src/library.rs:421).
Two passes:
-
Owned pass (
src/library.rs:432): for eachOwnedGame(skipping tooling), look up itsInstalledAppInfoin the installed map.is_installed= whether an install path was found.install_path,active_branchcome from the manifest if installed. These rows are markedis_owned = true. -
Installed-only pass (
src/library.rs:462): any app id present on disk but absent from the owned set is not licensed to this account. Markedis_owned = false. If its manifestLastOwnerdiffers from the logged-insteam_id, it is flaggedis_family_shared = true(src/library.rs:475). When the owner cannot be determined (not logged in, or noLastOwner), Family Sharing is not guessed, which avoids false positives.
owned_app_ids: HashSet (src/library.rs:429) lets the second pass skip already-emitted
ids in O(1). Result is sorted by name (src/library.rs:494).
online_required and platform are left None here. online_required is populated
later on demand (see §2.4).
merge_games(owned: Vec<OwnedGame>, installed: Vec<LocalGame>) → Vec<GameModel>
(src/library.rs:498). A lighter merge into GameModel keyed by app id: owned games seed
the map, and locals fill in install_dir/proton_version (and the name when the owned name
is blank). Used where the richer LibraryGame (ownership/family/branch flags) is not
needed.
build_game_library does not compute it. online_required is filled by
aurelia list --online (src/main.rs:1599), which calls
client.fetch_online_required(app_id) → steam_client::category_online_required
(src/steam_client.rs:734): a game is "online required" when it has online
multiplayer/MMO/online-co-op categories and no single-player category. Requires an
authenticated, online session. Otherwise the column is left unknown (src/main.rs:1608).
This is the installed games side. Entry points:
| Function | File:line | Returns |
|---|---|---|
scan_installed_app_info |
src/library.rs:87 |
HashMap<u32, InstalledAppInfo> (richest) |
scan_installed_app_paths |
src/library.rs:119 |
HashMap<u32, String> (path strings) |
scan_installed_app_paths_pathbuf |
src/library.rs:127 |
HashMap<u32, PathBuf> |
find_local_games |
src/library.rs:73 |
Vec<LocalGame> |
scan_library_info |
src/library.rs:135 |
scan one root + its sibling libraries |
all_library_roots |
src/library.rs:228 |
every discoverable library root |
Root resolution order (scan_installed_app_info, src/library.rs:87):
-
LauncherConfig.steam_library_pathif it contains asteamapps(orSteam/steamapps) dir (src/library.rs:89). -
detect_steam_path()(auto-detect). -
default_steam_root()(src/library.rs:258): platform default (~/.steam/steamon Linux,Program Files (x86)\Steamon Windows).
If windows_steam_discovery_enabled is set, it additionally scans a Windows-Steam
install inside a wine prefix at drive_c/Program Files (x86)/Steam
(src/library.rs:100-114), but prefers the native Linux install on duplicates
(installed.entry(app_id).or_insert(info)).
Library-folder enumeration (scan_library_info, src/library.rs:135): starts with
the given root, then adds:
- folders from
steamapps/libraryfolders.vdfviaparse_library_folders(src/library.rs:292), - a probe of all connected drives via
discover_drive_libraries(src/library.rs:191, Windows-only body that checksSteamLibrary,SteamGames, etc. on each drive letter for asteamappsdir).
The list is sorted/deduped, and for each root every appmanifest_*.acf
(is_app_manifest, src/library.rs:284) is parsed.
libraryfolders.vdf parsing (parse_library_folders, src/library.rs:292): uses
keyvalues-serde to deserialize into LibraryFoldersFile (src/library.rs:45). The
LibraryFolderRecord enum (src/library.rs:51, #[serde(untagged)]) tolerates both the
legacy "<n>" "<path>" form and the detailed { "path": ... } form. Only
numeric keys are accepted (src/library.rs:306).
appmanifest parsing (parse_app_manifest_info, src/library.rs:324): a hand-rolled
line scanner (not full VDF) using extract_quoted_values (src/library.rs:401). It
reads appid, installdir, name, LastOwner, StateFlags, and (inside the
userconfig block) BetaKey → active_branch. Two correctness rules:
-
LastOwner == 0is treated as "unknown" and dropped (src/library.rs:364). -
Installed gate: only counted installed if
StateFlags & 4(StateFullyInstalled) is set (src/library.rs:383), which prevents a cancelled partial download (StateUpdateRequired only) from reporting as installed. -
Install path =
<manifest dir>/common/<installdir>(src/library.rs:386).
| Type | File:line | Notes |
|---|---|---|
InstalledAppInfo |
src/library.rs:63 |
install_path, active_branch, name, last_owner (SteamID64 of the install's owner, which differs from the logged-in user for Family-Shared). |
LibraryFoldersFile / LibraryFolderRecord
|
src/library.rs:44, :50
|
serde shapes for libraryfolders.vdf. |
Purpose (module doc, src/local_library.rs:1): when Aurelia has no session / is
offline, reconstruct the full owned library by reading the Steam client's local
caches, with no network and no aurelia login required. On Linux the desktop Steam client is
almost always signed in and keeps the whole library cached on disk.
| Source | Path | Used for |
|---|---|---|
appinfo.vdf (binary) |
appcache/appinfo.vdf |
resolve app name + type, keep only type == "game". |
localconfig.vdf (text) |
userdata/<id3>/config/localconfig.vdf |
candidate app ids + Playtime (minutes). |
librarycache/ |
appcache/librarycache/<appid>/ |
broaden candidate set (Steam pre-fetches owned-title art). |
loginusers.vdf (text) |
config/loginusers.vdf |
find the MostRecent signed-in account. |
discover_local_owned_games() (src/local_library.rs:40) → discover_from_root
(src/local_library.rs:48). Never errors, and returns empty on missing/unreadable caches so
callers use it as best-effort.
-
steam_install_root()(src/local_library.rs:32):detect_steam_path()filtered to a dir that actually has anappcache/(the install root, not an arbitrary library folder). -
read_local_playtime(src/local_library.rs:91) →locate_localconfig(src/local_library.rs:103): prefer theMostRecentuser (most_recent_account_id,src/local_library.rs:127), else anylocalconfig.vdf. The 32-bit account id (userdata/<id>) is the SteamID64 minusSTEAMID64_BASE(76561197960265728,src/local_library.rs:27,account_id_from_id64). - Candidate set = playtime keys ∪
read_librarycache_appids(src/local_library.rs:162). -
parse_appinfo(src/local_library.rs:241) resolves names/types. Keep onlytype == "game"(src/local_library.rs:72). EmitsOwnedGamewith playtime (defaulting to0).
-
Text VDF:
parse_localconfig_apps(src/local_library.rs:182) walks brace depth, treating any direct child key of anappsobject as an app id and capturingPlaytime. Tokenized byquoted_tokens(src/local_library.rs:438). -
Binary
appinfo.vdf:parse_appinfo(src/local_library.rs:241) supports container versions0x27/0x28/0x29.0x28adds a per-record VDF sha1.0x29interns keys in a trailing string table (parse_string_table,src/local_library.rs:305). Each app record is length-prefixed.walk_object(src/local_library.rs:333) recursively reads the binary KV tree and pullsname/typeonly when directly inside acommonobject. Robust: malformed records are skipped, unknown header → empty map. Round-trip test atsrc/local_library.rs:505.
local_library.rsdoes not know install paths. It only reconstructs the owned set. Install paths come fromlibrary.rs's appmanifest scan.
This is not a game list. It resolves the CM (Connection Manager) endpoints, i.e.
the TCP servers that speak Steam's binary protocol, which the Steam client
(steam_client) dials to authenticate and fetch data.
| Item | File:line | Notes |
|---|---|---|
get_cm_endpoints() |
src/cm_list.rs:24 |
Public entry. Tries the dynamic list and falls back to hard-coded on failure/empty. |
fetch_dynamic_cm_list() |
src/cm_list.rs:31 |
GETs ISteamDirectory/GetCMListForConnect/v1/?cellid=0&maxcount=20, parses the response.serverlist of "ip:port" strings into SocketAddr. |
fallback_cm_endpoints() |
src/cm_list.rs:49 |
Parses DEFAULT_CM_ENDPOINTS (src/cm_list.rs:5, five :27017 IPs). |
CmListResponseEnvelope / CmListResponse
|
src/cm_list.rs:13, :18
|
serde shapes for the Steam Directory JSON. |
Consumer: src/steam_client/client.rs:193 calls get_cm_endpoints().await to obtain the
TCP server pool before connecting.
| Type | File:line | Role |
|---|---|---|
OwnedGame |
src/models.rs:142 |
account-licensed game (network or local-cache derived). |
LocalGame |
src/models.rs:161 |
one installed game (install_dir: PathBuf, active_branch). |
GameModel |
src/models.rs:175 |
simple merged model from merge_games. |
LibraryGame |
src/models.rs:191 |
rich list row: is_installed, install_path, is_owned, is_family_shared, active_branch, online_required, platform. |
GameLibrary |
src/models.rs:231 |
{ games: Vec<LibraryGame> }. |
aurelia list (src/main.rs:1160-1163, load_library)
owned = fetch_owned_games (network) OR local_library::discover_local_owned_games (offline fallback)
inst = library::scan_installed_app_info
library::build_game_library(owned, inst, steam_id) ─► GameLibrary
[--online] steam_client::category_online_required fills online_required (src/main.rs:1599)
merge_family_shared (src/main.rs:1169)
steam_client / manage / manifests
library::all_library_roots (src/steam_client.rs:750, manage.rs:156, manifests.rs:12)
steam_client::client
cm_list::get_cm_endpoints (src/steam_client/client.rs:193)
A launch needs two paths: the install directory (game files / exe cwd) and the
prefix location (the wine/Proton prefix umu's WINEPREFIX/GAMEID points at).
Both are already resolvable in this codebase:
Resolved by the appmanifest scan in library.rs:
- Source of truth:
parse_app_manifest_infobuildsinstall_path = <manifest dir>/common/<installdir>(src/library.rs:386), surfaced asInstalledAppInfo.install_path(src/library.rs:64) and copied ontoLibraryGame.install_path(src/library.rs:438,:482). - For a given app id, use
scan_installed_app_paths_pathbuf(src/library.rs:127) or readLibraryGame.install_pathdirectly. The launch pipeline already does this:PipelineContext.resolved_install_dir(src/launch/pipeline.rs:88) is set fromapp.install_pathinpreflight(src/launch/stages/preflight.rs:66), andresolve_components/resolve_dll_providersjoin the exe onto it (src/launch/stages/resolve_components.rs:25-32,src/launch/stages/resolve_dll_providers.rs:31). A missing install path is aLaunchErrorKind::GameData("Install path missing"). - Note the install path can live on any library root, not just the main Steam dir
(multiple
steamappsroots vialibraryfolders.vdf+ drive probing). Always derive it from the scanned manifest, never assume~/.steam/steam/steamapps/common.
Prefix resolution is not in these three modules. It is utils::steam_wineprefix_for_game
(src/utils.rs:1225):
-
Per-game prefix (
SteamPrefixMode::PerGame/use_shared_compat_data):<steam_library_path>/steamapps/compatdata/<app_id>/pfx(src/utils.rs:1244-1249). This is the standard Steam/Proton layout and the naturalWINEPREFIXfor umu (umu typically managescompatdata/<id>/pfx). -
Shared/master prefix:
resolve_master_wineprefix()(src/utils.rs:1251), backed byget_master_steam_config().wine_prefix(src/utils.rs:869).
The prefix path is selected per app from UserConfigStore (steam-runtime policy + prefix
mode). The launch pipeline calls it in prepare_prefix
(src/launch/stages/prepare_prefix.rs:36).
-
active_branch(InstalledAppInfo.active_branch, from manifestBetaKey), if a launch must match the installed beta branch. -
last_owner/is_family_shared(src/library.rs:70,:475): a Family-Shared install is owned by a different SteamID64, which is relevant if umu/Proton needs the licensing account.
| umu input | Where to get it |
|---|---|
STORE=steam, game files / exe cwd |
LibraryGame.install_path ← scan_installed_app_info (src/library.rs:87) |
WINEPREFIX |
utils::steam_wineprefix_for_game (src/utils.rs:1225): compatdata/<app_id>/pfx in per-game mode |
GAMEID / app id |
LibraryGame.app_id |
| native vs Proton decision |
LibraryGame.platform (src/models.rs:221) |
Users
-
Usage
- Global behavior
- Authentication
- Library
- Store & discovery
- Collections
- Install & maintenance
- Launching
- Depots & branches
- Downgrade & pinning
- Steam Cloud
- Steam Workshop
- Friends & chat
- Inventory & market
- Configuration
- Proton & Wine
- Windows Steam runtime
- Luxtorpeda plugin
- umu-launcher plugin
- Launch scripts
- Session daemon
- Files & locations
- Exit codes & logging
- Windows Steam Runtime
Maintainers
Architecture