refactor(peer): make startup directory-driven
Peer startup used to bootstrap itself by spawning the runtime and immediately sending a SetGameDir command back through its own control channel. The Tauri integration then polled shared state until a directory appeared and waited two seconds before asking peers for games. That made startup ordering implicit and left a race-prone sleep in the UI bridge. Install the initial game directory directly into the peer context instead. The runtime now attempts the initial local-library scan before starting discovery, then launches the server, discovery, liveness, and local monitor services from that initialized context. Later directory changes still use SetGameDir, so the existing UI command surface stays intact. Use PathBuf and Path references across peer filesystem boundaries so directory state is represented as a path rather than an optional string. The Tauri layer now validates a selected game directory before storing it, loads the bundled catalog on first use, and starts or updates the peer runtime from one helper. Peer event fan-out is split into named handlers so the Tauri setup closure only wires state and starts the event loop. Shutdown goodbye notifications are still best-effort, but they are now awaited with a short timeout instead of being spawned and forgotten. The tradeoff is a small bounded wait during peer runtime shutdown in exchange for clearer task ownership. Test Plan: - cargo test -p lanspread-peer - cargo clippy - cargo clippy --benches - cargo clippy --tests - cargo +nightly fmt - git diff --check Refs: none
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! Command handlers for peer commands.
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
|
||||
|
||||
use lanspread_db::db::GameFileDescription;
|
||||
use tokio::sync::{RwLock, mpsc::UnboundedSender};
|
||||
@@ -39,9 +39,6 @@ async fn try_serve_local_game(
|
||||
id: &str,
|
||||
) -> bool {
|
||||
let game_dir = { ctx.game_dir.read().await.clone() };
|
||||
let Some(game_dir) = game_dir else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let downloading = ctx.downloading_games.read().await;
|
||||
if !local_download_available(&game_dir, id, &downloading).await {
|
||||
@@ -145,10 +142,6 @@ pub async fn handle_download_game_files_command(
|
||||
) {
|
||||
log::info!("Got PeerCommand::DownloadGameFiles");
|
||||
let games_folder = { ctx.game_dir.read().await.clone() };
|
||||
let Some(games_folder) = games_folder else {
|
||||
log::error!("Cannot handle game file descriptions: games_folder is not set");
|
||||
return;
|
||||
};
|
||||
|
||||
// Use majority validation to get trusted file descriptions and peer whitelist
|
||||
let (validated_descriptions, peer_whitelist, file_peer_map) = {
|
||||
@@ -264,22 +257,17 @@ pub async fn handle_download_game_files_command(
|
||||
pub async fn handle_set_game_dir_command(
|
||||
ctx: &Ctx,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
game_dir: String,
|
||||
game_dir: PathBuf,
|
||||
) {
|
||||
*ctx.game_dir.write().await = Some(game_dir.clone());
|
||||
log::info!("Game directory set to: {game_dir}");
|
||||
*ctx.game_dir.write().await = game_dir.clone();
|
||||
log::info!("Game directory set to: {}", game_dir.display());
|
||||
|
||||
// Load local game database when game directory is set
|
||||
let game_dir = game_dir.clone();
|
||||
let tx_notify_ui = tx_notify_ui.clone();
|
||||
let ctx_clone = ctx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
match scan_local_library(&game_dir).await {
|
||||
Ok(scan) => {
|
||||
update_and_announce_games(&ctx_clone, &tx_notify_ui, scan).await;
|
||||
log::info!("Local game database loaded successfully");
|
||||
}
|
||||
match load_local_library(&ctx_clone, &tx_notify_ui).await {
|
||||
Ok(()) => log::info!("Local game database loaded successfully"),
|
||||
Err(e) => {
|
||||
log::error!("Failed to load local game database: {e}");
|
||||
}
|
||||
@@ -287,6 +275,17 @@ pub async fn handle_set_game_dir_command(
|
||||
});
|
||||
}
|
||||
|
||||
/// Loads the configured local library and announces the result.
|
||||
pub async fn load_local_library(
|
||||
ctx: &Ctx,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) -> eyre::Result<()> {
|
||||
let game_dir = { ctx.game_dir.read().await.clone() };
|
||||
let scan = scan_local_library(&game_dir).await?;
|
||||
update_and_announce_games(ctx, tx_notify_ui, scan).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles the `GetPeerCount` command.
|
||||
pub async fn handle_get_peer_count_command(ctx: &Ctx, tx_notify_ui: &UnboundedSender<PeerEvent>) {
|
||||
log::info!("GetPeerCount command received");
|
||||
|
||||
Reference in New Issue
Block a user