detect if a game is deleted, added, modified locally

This commit is contained in:
2025-11-14 01:12:01 +01:00
parent 4764bb9fd3
commit 8432030292
2 changed files with 136 additions and 1 deletions
+93 -1
View File
@@ -5,7 +5,7 @@ mod peer;
use std::{
cmp::Reverse,
collections::{HashMap, VecDeque},
collections::{HashMap, HashSet, VecDeque},
net::{IpAddr, SocketAddr},
path::{Path, PathBuf},
sync::Arc,
@@ -130,6 +130,7 @@ pub enum PeerEvent {
PeerDiscovered(SocketAddr),
PeerLost(SocketAddr),
PeerCountUpdated(usize),
LocalGamesUpdated(Vec<Game>),
}
#[derive(Clone, Debug)]
@@ -1265,6 +1266,7 @@ async fn local_download_available(game_dir: &str, game_id: &str) -> bool {
!local_dir_has_content(game_path.as_path()).await
}
#[derive(Clone)]
struct Ctx {
game_dir: Arc<RwLock<Option<String>>>,
local_game_db: Arc<RwLock<Option<GameDB>>>,
@@ -1345,6 +1347,13 @@ pub async fn run_peer(
run_ping_service(tx_notify_ui_ping, peer_game_db_ping).await;
});
// Start local game directory monitoring task
let tx_notify_ui_monitor = tx_notify_ui.clone();
let ctx_monitor = ctx.clone();
tokio::spawn(async move {
run_local_game_monitor(tx_notify_ui_monitor, ctx_monitor).await;
});
// Handle client commands
loop {
let Some(cmd) = rx_control.recv().await else {
@@ -2170,6 +2179,89 @@ async fn run_ping_service(
}
}
/// Monitor local game directory for changes and update the local game database
async fn run_local_game_monitor(tx_notify_ui: UnboundedSender<PeerEvent>, ctx: Ctx) {
log::info!("Starting local game directory monitor (5s interval)");
let mut interval = tokio::time::interval(Duration::from_secs(5));
loop {
interval.tick().await;
let game_dir = {
let guard = ctx.game_dir.read().await;
guard.clone()
};
if let Some(ref game_dir) = game_dir {
match scan_local_games(game_dir).await {
Ok(current_games) => {
let local_game_db = ctx.local_game_db.clone();
let mut db_guard = local_game_db.write().await;
let previous_games = db_guard
.as_ref()
.map(|db| db.games.keys().cloned().collect::<HashSet<_>>())
.unwrap_or_default();
let current_game_ids =
current_games.games.keys().cloned().collect::<HashSet<_>>();
// Check if any games were removed
let removed_games: Vec<String> = previous_games
.difference(&current_game_ids)
.cloned()
.collect();
if removed_games.is_empty() {
// Check if any games were added or updated
if previous_games != current_game_ids {
log::debug!(
"Local games directory structure changed, updating database"
);
*db_guard = Some(current_games);
let all_games = db_guard
.as_ref()
.map(|db| {
db.all_games().into_iter().cloned().collect::<Vec<Game>>()
})
.unwrap_or_default();
if let Err(e) =
tx_notify_ui.send(PeerEvent::LocalGamesUpdated(all_games))
{
log::error!("Failed to send LocalGamesUpdated event: {e}");
}
}
} else {
log::info!("Detected removed games: {removed_games:?}");
*db_guard = Some(current_games);
// Notify UI about the change
let all_games = db_guard
.as_ref()
.map(|db| db.all_games().into_iter().cloned().collect::<Vec<Game>>())
.unwrap_or_default();
if let Err(e) = tx_notify_ui.send(PeerEvent::LocalGamesUpdated(all_games)) {
log::error!("Failed to send LocalGamesUpdated event: {e}");
}
}
}
Err(e) => {
log::error!("Failed to scan local games directory: {e}");
}
}
}
}
}
/// Scan the local games directory and return a `GameDB` with current games
async fn scan_local_games(game_dir: &str) -> eyre::Result<GameDB> {
load_local_game_db(game_dir).await
}
async fn ping_peer(peer_addr: SocketAddr) -> eyre::Result<bool> {
let limits = Limits::default().with_max_handshake_duration(Duration::from_secs(3))?;