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:
+97
-12
File diff suppressed because one or more lines are too long
@@ -43,6 +43,22 @@ export const DeviceRef = S.Struct({
|
||||
});
|
||||
export type DeviceRef = S.Schema.Type<typeof DeviceRef>;
|
||||
|
||||
/** A launched game, as the `game.*` events identify it. */
|
||||
export const GameRef = S.Struct({
|
||||
/** Store-qualified library id (`steam:570`). Absent for an operator-typed GameStream command. */
|
||||
app: S.optional(S.String),
|
||||
title: S.String,
|
||||
store: S.optional(S.String),
|
||||
/** Client-supplied device name of the session that launched it; may be empty. */
|
||||
client: S.String,
|
||||
plane: Plane,
|
||||
});
|
||||
export type GameRef = S.Schema.Type<typeof GameRef>;
|
||||
|
||||
/** Why a launched game is no longer running. */
|
||||
export const GameEndReason = S.Literals(["exited", "terminated"]);
|
||||
export type GameEndReason = S.Schema.Type<typeof GameEndReason>;
|
||||
|
||||
/** The `{seq, ts_ms, schema}` envelope every event carries. */
|
||||
const envelope = {
|
||||
seq: S.Number,
|
||||
@@ -81,6 +97,26 @@ export const StreamStopped = S.Struct({
|
||||
kind: S.Literal("stream.stopped"),
|
||||
stream: StreamRef,
|
||||
});
|
||||
/**
|
||||
* A launched game's process was seen running — not merely its launcher spawned. Fires once per
|
||||
* session that launched a title.
|
||||
*/
|
||||
export const GameRunning = S.Struct({
|
||||
...envelope,
|
||||
kind: S.Literal("game.running"),
|
||||
game: GameRef,
|
||||
});
|
||||
/**
|
||||
* A launched game is gone. `reason` separates the player quitting (`exited` — which is what ends the
|
||||
* streaming session, when that is enabled) from the host ending it per the lifetime policy
|
||||
* (`terminated`).
|
||||
*/
|
||||
export const GameExited = S.Struct({
|
||||
...envelope,
|
||||
kind: S.Literal("game.exited"),
|
||||
game: GameRef,
|
||||
reason: GameEndReason,
|
||||
});
|
||||
export const PairingPending = S.Struct({
|
||||
...envelope,
|
||||
kind: S.Literal("pairing.pending"),
|
||||
@@ -131,6 +167,8 @@ export const HostEvent = S.Union([
|
||||
SessionEnded,
|
||||
StreamStarted,
|
||||
StreamStopped,
|
||||
GameRunning,
|
||||
GameExited,
|
||||
PairingPending,
|
||||
PairingCompleted,
|
||||
PairingDenied,
|
||||
|
||||
Reference in New Issue
Block a user