feat(game): broadcast game presence (GOSSIP_PROTO 3) + per-game background
Steps 5-6 of game detection. BREAKING wire change — bump everyone. Wire (step 5): - PeerState.game: Option<String> (display label only — never appid/source). - SelfPresence.game + to_state carry it (single self-state builder). - GOSSIP_PROTO 2->3, GOSSIP_SIG_DOMAIN v3, version comment bumped together; Cargo MINOR 0.3.0 -> 0.4.0 per VERSIONING.md. v2/v3 isolate into different topics + signature domains, so a coordinated redeploy is required (same as the W4 avatar bump). - Gossip ingest sanitizes incoming game via sanitize_game_label (bidi/ control strip, 64-char/256-byte cap); empty -> None. - Bonus security fix (Codex find): reject inbound gossip frames over a 128KB cap BEFORE serde_json::from_slice — a legit Announce with a full 48KB avatar is ~49KB, so this bounds allocation abuse with headroom. Core wiring: - Spawns the detector at startup; consumes its watch channel in the main select. Detection runs continuously (for the local background); the broadcast is gated by game_presence_enabled (opt-in, default OFF). New commands: SetGamePresenceEnabled (immediate publish/clear, D8), SetGameOverride, SetGameProcessMap. New event: GameChanged. - game_presence_label sanitizes the outgoing label too. Background switch (step 6): - GUI handles GameChanged: stores current_game, swaps background to the per-game override (config.game_backgrounds[id]) or falls back to the W16 default; reuses the existing cached-handle path (no redraw flicker). 397 lib tests (all green), clippy --all-targets clean, full binary builds. Remaining: step 7 UI (opt-in toggle, roster 'Playing' text, manual override control, Settings game-backgrounds + process-map editors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,17 @@ pub enum CoreCommand {
|
||||
/// invisible) and the outbound ping scheduler (invisible = fully dark). Sent at
|
||||
/// startup from config and whenever the user changes it.
|
||||
SetPresenceMode(PresenceMode),
|
||||
/// Toggle broadcasting the detected game as presence (game detection). Opt-in,
|
||||
/// default OFF. Enabling immediately publishes the current game; disabling
|
||||
/// immediately publishes `game: None`. Detection for the local background runs
|
||||
/// regardless. Sent at startup from config and on user toggle.
|
||||
SetGamePresenceEnabled(bool),
|
||||
/// Set the manual game-detection override (`Auto` / `None` / a forced game).
|
||||
/// Forwarded to the detector and applied immediately (bypasses debounce).
|
||||
SetGameOverride(crate::game::ManualOverride),
|
||||
/// Replace the user process→display-name mappings used by the non-Steam
|
||||
/// detection fallback. Sent at startup from config and after Settings edits.
|
||||
SetGameProcessMap(std::collections::BTreeMap<String, String>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -150,6 +161,12 @@ pub enum UiEvent {
|
||||
/// failure, this carries the previous truthful mode. The GUI must mirror +
|
||||
/// persist this so its presence picker matches the endpoint's discovery state.
|
||||
PresenceModeReverted { mode: PresenceMode },
|
||||
/// The locally-detected running game changed (game detection). Carries the
|
||||
/// debounced `DetectedGame` (id + display name + source) or `None` when nothing
|
||||
/// is detected. The GUI uses the stable `id` to switch the per-game background
|
||||
/// (W18) and may show a local "Playing …" indicator. Emitted regardless of
|
||||
/// whether game presence is being broadcast — the broadcast is core's own job.
|
||||
GameChanged(Option<crate::game::DetectedGame>),
|
||||
/// Core finished orderly app shutdown and the GUI can exit.
|
||||
ShutdownComplete,
|
||||
Error(String),
|
||||
|
||||
Reference in New Issue
Block a user