feat(library/providers): let a provider say how to recognize its games
A plugin's titles launch through the provider's own client, which hands off and exits — so the host had nothing left to watch, and both lifetime behaviors went quiet for exactly the entries a provider contributes. A `ProviderEntry` (and a manual custom entry) may now carry an optional `detect` hint: install dir, exe, or process name. It is deliberately a subset of what the host tracks internally. A Steam appid or a launcher's environment marker are things the host discovers for itself and would be meaningless — or dangerous — to take on someone's word; where a title is installed is something only the provider knows. The host's own findings win where both exist, so a stale export can never redirect the matcher, and a blank field is treated as absent rather than as "match everything" — an empty install dir would otherwise prefix-match every process on the box, and this feature can end processes. `process_name` is the weakest of the three and the only one typed by hand, so it is matched case-insensitively against the image's file name and nothing else: `retroarch` finds RetroArch, not a helper whose name merely starts the same way, and not a script that happens to live in a `retroarch/` directory. The never-adopt-a-pre-existing-process rule still bounds it. Also: the tray summary gains the running-game row (with the closing-in countdown for a game whose client is gone — visible at the machine without opening the console), the SDK mirrors the `game.*` events, and its generated client catches up with the endpoints Phase 1 added. Gates on .21: check + clippy --all-targets clean, 299 tests, fmt CI-parity, openapi regenerated (GameEntry still carries no `detect` outbound); SDK tsc + 54 tests green.
This commit is contained in:
@@ -207,6 +207,13 @@ pub(crate) struct LocalSummary {
|
||||
/// the tray/console surface them so the clash is visible before pairing silently fails.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
conflicts: Vec<String>,
|
||||
/// Launched games the host is tracking, as compact labels (`Hades`, `Hades (closing in 4:12)`).
|
||||
///
|
||||
/// The countdown form is the one that matters: it means the game's client is gone and the host
|
||||
/// will end the game when the window closes — something a user at the machine should be able to
|
||||
/// see (and stop) without opening the console. Empty when nothing was launched.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
games: Vec<String>,
|
||||
}
|
||||
|
||||
/// Liveness probe
|
||||
@@ -489,5 +496,12 @@ pub(crate) async fn get_local_summary(State(st): State<Arc<MgmtState>>) -> Json<
|
||||
// Cached at `serve` startup (empty when nothing was detected / never scanned) — no per-poll
|
||||
// process enumeration.
|
||||
conflicts: crate::detect::summary_labels(crate::detect::snapshot()),
|
||||
games: crate::session_status::games()
|
||||
.into_iter()
|
||||
.map(|g| match g.grace_remaining_s {
|
||||
Some(left) => format!("{} (closing in {}:{:02})", g.title, left / 60, left % 60),
|
||||
None => g.title,
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user