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:
@@ -55,7 +55,7 @@ pub async fn local_dir_has_content(path: &Path) -> bool {
|
||||
|
||||
/// Checks if a game is available for download locally.
|
||||
pub async fn local_download_available(
|
||||
game_dir: &str,
|
||||
game_dir: &Path,
|
||||
game_id: &str,
|
||||
downloading_games: &HashSet<String>,
|
||||
) -> bool {
|
||||
@@ -64,7 +64,7 @@ pub async fn local_download_available(
|
||||
return false;
|
||||
}
|
||||
|
||||
let game_path = PathBuf::from(game_dir).join(game_id);
|
||||
let game_path = game_dir.join(game_id);
|
||||
let eti_path = game_path.join(format!("{game_id}.eti"));
|
||||
|
||||
if tokio::fs::metadata(&eti_path).await.is_err() {
|
||||
@@ -109,10 +109,8 @@ pub struct LocalLibraryScan {
|
||||
pub revision: u64,
|
||||
}
|
||||
|
||||
fn library_index_path(game_dir: &str) -> PathBuf {
|
||||
PathBuf::from(game_dir)
|
||||
.join(LIBRARY_INDEX_DIR)
|
||||
.join(LIBRARY_INDEX_FILE)
|
||||
fn library_index_path(game_dir: &Path) -> PathBuf {
|
||||
game_dir.join(LIBRARY_INDEX_DIR).join(LIBRARY_INDEX_FILE)
|
||||
}
|
||||
|
||||
async fn load_library_index(path: &Path) -> LibraryIndex {
|
||||
@@ -408,10 +406,10 @@ fn empty_scan() -> LocalLibraryScan {
|
||||
// =============================================================================
|
||||
|
||||
/// Scans the local game directory and returns summaries plus a game database.
|
||||
pub async fn scan_local_library(game_dir: &str) -> eyre::Result<LocalLibraryScan> {
|
||||
let game_path = PathBuf::from(game_dir);
|
||||
pub async fn scan_local_library(game_dir: impl AsRef<Path>) -> eyre::Result<LocalLibraryScan> {
|
||||
let game_path = game_dir.as_ref();
|
||||
|
||||
let metadata = match tokio::fs::metadata(&game_path).await {
|
||||
let metadata = match tokio::fs::metadata(game_path).await {
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => {
|
||||
if err.kind() == ErrorKind::NotFound {
|
||||
@@ -433,14 +431,14 @@ pub async fn scan_local_library(game_dir: &str) -> eyre::Result<LocalLibraryScan
|
||||
return Ok(empty_scan());
|
||||
}
|
||||
|
||||
let index_path = library_index_path(game_dir);
|
||||
let index_path = library_index_path(game_path);
|
||||
let mut index = load_library_index(&index_path).await;
|
||||
let mut seen_ids = HashSet::new();
|
||||
let mut summaries = HashMap::new();
|
||||
let mut games = Vec::new();
|
||||
let mut changed = false;
|
||||
|
||||
let mut entries = tokio::fs::read_dir(&game_path).await?;
|
||||
let mut entries = tokio::fs::read_dir(game_path).await?;
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
let path = entry.path();
|
||||
if !path.is_dir() {
|
||||
@@ -451,7 +449,7 @@ pub async fn scan_local_library(game_dir: &str) -> eyre::Result<LocalLibraryScan
|
||||
continue;
|
||||
};
|
||||
|
||||
let update = update_index_for_game(&game_path, game_id, &mut index).await?;
|
||||
let update = update_index_for_game(game_path, game_id, &mut index).await?;
|
||||
changed |= update.changed;
|
||||
|
||||
let Some(summary) = update.summary else {
|
||||
@@ -493,7 +491,7 @@ pub async fn scan_local_library(game_dir: &str) -> eyre::Result<LocalLibraryScan
|
||||
/// Gets file descriptions for a game from the local filesystem.
|
||||
pub async fn get_game_file_descriptions(
|
||||
game_id: &str,
|
||||
game_dir: &str,
|
||||
game_dir: impl AsRef<Path>,
|
||||
) -> Result<Vec<GameFileDescription>, PeerError> {
|
||||
scan_game_descriptions(game_id, &PathBuf::from(game_dir)).await
|
||||
scan_game_descriptions(game_id, game_dir.as_ref()).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user