detect if a game is deleted, added, modified locally
This commit is contained in:
@@ -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(¤t_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))?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user