fix(peer): settle current-protocol local state cleanup
The follow-up backlog had drifted into three settled peer/runtime issues: the legacy game-list fallback contradicted the one-wire-version policy, the Tauri shell still re-derived local install state from disk after peer snapshots, and `Availability::Downloading` existed even though active operations are already reported through a separate operation table. Remove the legacy `AnnounceGames` request and fallback service. Discovery now ignores peers that do not advertise the current protocol and a peer id, and library changes are sent through the current delta path only. This keeps the runtime aligned with the documented current-build-only interoperability model. Make peer `LocalGamesUpdated` snapshots authoritative for local fields in the Tauri database. The GUI-side catalog still owns static metadata such as names, sizes, and descriptions, but downloaded, installed, local version, and availability now come from the peer runtime instead of a second whole-library filesystem scan. Snapshot reconciliation also pins the missing-begin and missing-finish lifecycle cases in tests. Collapse availability back to the settled `Ready` and `LocalOnly` states. Aggregation now counts only `Ready` peers as download sources, and the frontend no longer carries a dead `Downloading` enum value. The core peer also exposes the small non-GUI hooks needed by scripted callers: startup options for state and mDNS, a local-ready event, direct connection, peer snapshots, and an explicit post-download install policy. Those hooks reuse the same current protocol path and do not add compatibility shims. Test Plan: - `git diff --check` - `just fmt` - `just clippy` - `just test` Refs: BACKLOG.md, FINDINGS.md, IMPL_DECISIONS.md
This commit is contained in:
@@ -7,9 +7,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
use lanspread_db::db::Availability;
|
||||
use lanspread_db::db::{Game, GameFileDescription};
|
||||
use lanspread_db::db::{Availability, Game, GameFileDescription};
|
||||
use lanspread_proto::{GameSummary, LibraryDelta, LibrarySnapshot};
|
||||
|
||||
use crate::library::compute_library_digest;
|
||||
@@ -36,6 +34,18 @@ pub struct PeerInfo {
|
||||
pub files: HashMap<String, Vec<GameFileDescription>>,
|
||||
}
|
||||
|
||||
/// Immutable peer state suitable for CLI assertions and tests.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PeerSnapshot {
|
||||
pub peer_id: PeerId,
|
||||
pub addr: SocketAddr,
|
||||
pub library_rev: u64,
|
||||
pub library_digest: u64,
|
||||
pub features: Vec<String>,
|
||||
pub game_count: usize,
|
||||
pub games: Vec<GameSummary>,
|
||||
}
|
||||
|
||||
/// Database tracking all discovered peers and their games.
|
||||
#[derive(Debug)]
|
||||
pub struct PeerGameDB {
|
||||
@@ -363,6 +373,30 @@ impl PeerGameDB {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns immutable snapshots for all known peers.
|
||||
#[must_use]
|
||||
pub fn peer_snapshots(&self) -> Vec<PeerSnapshot> {
|
||||
let mut peers = self
|
||||
.peers
|
||||
.values()
|
||||
.map(|peer| {
|
||||
let mut games = peer.games.values().cloned().collect::<Vec<_>>();
|
||||
games.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
PeerSnapshot {
|
||||
peer_id: peer.peer_id.clone(),
|
||||
addr: peer.addr,
|
||||
library_rev: peer.library_rev,
|
||||
library_digest: peer.library_digest,
|
||||
features: peer.features.clone(),
|
||||
game_count: games.len(),
|
||||
games,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
peers.sort_by(|a, b| a.peer_id.cmp(&b.peer_id));
|
||||
peers
|
||||
}
|
||||
|
||||
/// Checks if a peer is in the database.
|
||||
#[must_use]
|
||||
pub fn contains_peer(&self, peer_id: &PeerId) -> bool {
|
||||
@@ -744,7 +778,7 @@ fn create_peer_whitelist(peer_scores: HashMap<SocketAddr, usize>) -> Vec<SocketA
|
||||
}
|
||||
|
||||
fn game_is_ready(summary: &GameSummary) -> bool {
|
||||
summary.availability.is_downloaded()
|
||||
summary.availability == Availability::Ready
|
||||
}
|
||||
|
||||
fn summary_to_game(summary: &GameSummary) -> Game {
|
||||
@@ -762,7 +796,7 @@ fn summary_to_game(summary: &GameSummary) -> Game {
|
||||
version: "1.0".to_string(),
|
||||
genre: String::new(),
|
||||
size: summary.size,
|
||||
downloaded: summary.availability.is_downloaded(),
|
||||
downloaded: game_is_ready(summary),
|
||||
installed: summary.installed,
|
||||
availability: summary.availability.clone(),
|
||||
eti_game_version,
|
||||
|
||||
Reference in New Issue
Block a user