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:
@@ -2,17 +2,21 @@
|
||||
use std::fs::File;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
net::SocketAddr,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use eyre::bail;
|
||||
use lanspread_compat::eti::get_games;
|
||||
use lanspread_db::db::{Game, GameDB};
|
||||
use lanspread_db::db::{Game, GameDB, GameFileDescription};
|
||||
use lanspread_peer::{PeerCommand, PeerEvent, PeerGameDB, start_peer};
|
||||
use tauri::{AppHandle, Emitter as _, Manager};
|
||||
use tauri_plugin_shell::{ShellExt, process::Command};
|
||||
use tokio::sync::{RwLock, mpsc::UnboundedSender};
|
||||
use tokio::sync::{
|
||||
RwLock,
|
||||
mpsc::{UnboundedReceiver, UnboundedSender},
|
||||
};
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
|
||||
@@ -26,6 +30,8 @@ struct LanSpreadState {
|
||||
peer_game_db: Arc<RwLock<PeerGameDB>>,
|
||||
}
|
||||
|
||||
struct PeerEventTx(UnboundedSender<PeerEvent>);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const FIRST_START_DONE_FILE: &str = ".softlan_first_start_done";
|
||||
|
||||
@@ -613,37 +619,18 @@ async fn refresh_games_list(app_handle: &AppHandle) {
|
||||
async fn update_game_directory(app_handle: tauri::AppHandle, path: String) -> tauri::Result<()> {
|
||||
log::info!("update_game_directory: {path}");
|
||||
|
||||
let peer_ctrl_lock = app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.peer_ctrl
|
||||
.clone();
|
||||
let games_folder_lock = app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.games_folder
|
||||
.clone();
|
||||
|
||||
let peer_ctrl = peer_ctrl_lock.read().await.clone();
|
||||
|
||||
if let Some(peer_ctrl) = peer_ctrl
|
||||
&& let Err(e) = peer_ctrl.send(PeerCommand::SetGameDir(path.clone()))
|
||||
{
|
||||
log::error!("Failed to send PeerCommand::SetGameDir: {e}");
|
||||
}
|
||||
|
||||
{
|
||||
let mut games_folder = games_folder_lock.write().await;
|
||||
games_folder.clone_from(&path);
|
||||
}
|
||||
|
||||
let path = PathBuf::from(path);
|
||||
if !path.exists() {
|
||||
log::error!("game dir {} does not exist", path.display());
|
||||
let games_folder = PathBuf::from(&path);
|
||||
if !games_folder.is_dir() {
|
||||
log::error!("game dir {} does not exist", games_folder.display());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let state = app_handle.state::<LanSpreadState>();
|
||||
*state.games_folder.write().await = path;
|
||||
|
||||
ensure_bundled_game_db_loaded(&app_handle).await;
|
||||
refresh_games_list(&app_handle).await;
|
||||
ensure_peer_started(&app_handle, &games_folder).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -818,7 +805,233 @@ async fn load_bundled_game_db(app_handle: &AppHandle) -> GameDB {
|
||||
GameDB::from(games)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
async fn ensure_bundled_game_db_loaded(app_handle: &AppHandle) {
|
||||
let state = app_handle.state::<LanSpreadState>();
|
||||
let needs_load = { state.games.read().await.games.is_empty() };
|
||||
|
||||
if needs_load {
|
||||
let game_db = load_bundled_game_db(app_handle).await;
|
||||
*state.games.write().await = game_db;
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_peer_started(app_handle: &AppHandle, games_folder: &Path) {
|
||||
let state = app_handle.state::<LanSpreadState>();
|
||||
let mut peer_ctrl = state.peer_ctrl.write().await;
|
||||
|
||||
if let Some(peer_ctrl) = peer_ctrl.as_ref() {
|
||||
if let Err(e) = peer_ctrl.send(PeerCommand::SetGameDir(games_folder.to_path_buf())) {
|
||||
log::error!("Failed to send PeerCommand::SetGameDir: {e}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let tx_peer_event = app_handle.state::<PeerEventTx>().inner().0.clone();
|
||||
match start_peer(
|
||||
games_folder.to_path_buf(),
|
||||
tx_peer_event,
|
||||
state.peer_game_db.clone(),
|
||||
) {
|
||||
Ok(new_peer_ctrl) => {
|
||||
*peer_ctrl = Some(new_peer_ctrl.clone());
|
||||
if let Err(e) = new_peer_ctrl.send(PeerCommand::ListGames) {
|
||||
log::error!("Failed to send initial PeerCommand::ListGames: {e}");
|
||||
}
|
||||
log::info!("Peer system initialized successfully with games directory");
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to initialize peer system: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_game_id_event(app_handle: &AppHandle, event: &str, id: &str, label: &str) {
|
||||
if let Err(e) = app_handle.emit(event, Some(id.to_owned())) {
|
||||
log::error!("{label}: Failed to emit {event} event: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_peer_addr_event(app_handle: &AppHandle, event: &str, addr: SocketAddr) {
|
||||
if let Err(e) = app_handle.emit(event, Some(addr.to_string())) {
|
||||
log::error!("Failed to emit {event} event: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_peer_event_loop(app_handle: AppHandle, mut rx_peer_event: UnboundedReceiver<PeerEvent>) {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(event) = rx_peer_event.recv().await {
|
||||
handle_peer_event(&app_handle, event).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn handle_peer_event(app_handle: &AppHandle, event: PeerEvent) {
|
||||
match event {
|
||||
PeerEvent::ListGames(games) => {
|
||||
log::info!("PeerEvent::ListGames received");
|
||||
update_game_db(games, app_handle.clone()).await;
|
||||
}
|
||||
PeerEvent::LocalGamesUpdated(local_games) => {
|
||||
log::info!("PeerEvent::LocalGamesUpdated received");
|
||||
update_local_games_in_db(local_games, app_handle.clone()).await;
|
||||
}
|
||||
PeerEvent::GotGameFiles {
|
||||
id,
|
||||
file_descriptions,
|
||||
} => {
|
||||
handle_got_game_files(app_handle, id, file_descriptions).await;
|
||||
}
|
||||
PeerEvent::NoPeersHaveGame { id } => {
|
||||
log::warn!("PeerEvent::NoPeersHaveGame received for {id}");
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-no-peers",
|
||||
&id,
|
||||
"PeerEvent::NoPeersHaveGame",
|
||||
);
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.remove(&id);
|
||||
}
|
||||
PeerEvent::DownloadGameFilesBegin { id } => {
|
||||
log::info!("PeerEvent::DownloadGameFilesBegin received");
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.insert(id.clone());
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-download-begin",
|
||||
&id,
|
||||
"PeerEvent::DownloadGameFilesBegin",
|
||||
);
|
||||
}
|
||||
PeerEvent::DownloadGameFilesFinished { id } => {
|
||||
handle_download_finished(app_handle, id).await;
|
||||
}
|
||||
PeerEvent::DownloadGameFilesFailed { id } => {
|
||||
log::warn!("PeerEvent::DownloadGameFilesFailed received");
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-download-failed",
|
||||
&id,
|
||||
"PeerEvent::DownloadGameFilesFailed",
|
||||
);
|
||||
cleanup_failed_download(app_handle, &id).await;
|
||||
}
|
||||
PeerEvent::DownloadGameFilesAllPeersGone { id } => {
|
||||
log::warn!("PeerEvent::DownloadGameFilesAllPeersGone received for {id}");
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-download-peers-gone",
|
||||
&id,
|
||||
"PeerEvent::DownloadGameFilesAllPeersGone",
|
||||
);
|
||||
cleanup_failed_download(app_handle, &id).await;
|
||||
}
|
||||
PeerEvent::PeerConnected(addr) => {
|
||||
log::info!("Peer connected: {addr}");
|
||||
emit_peer_addr_event(app_handle, "peer-connected", addr);
|
||||
}
|
||||
PeerEvent::PeerDisconnected(addr) => {
|
||||
log::info!("Peer disconnected: {addr}");
|
||||
emit_peer_addr_event(app_handle, "peer-disconnected", addr);
|
||||
}
|
||||
PeerEvent::PeerDiscovered(addr) => {
|
||||
log::info!("Peer discovered: {addr}");
|
||||
emit_peer_addr_event(app_handle, "peer-discovered", addr);
|
||||
}
|
||||
PeerEvent::PeerLost(addr) => {
|
||||
log::info!("Peer lost: {addr}");
|
||||
emit_peer_addr_event(app_handle, "peer-lost", addr);
|
||||
}
|
||||
PeerEvent::PeerCountUpdated(count) => {
|
||||
log::info!("Peer count updated: {count}");
|
||||
if let Err(e) = app_handle.emit("peer-count-updated", Some(count)) {
|
||||
log::error!("Failed to emit peer-count-updated event: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_got_game_files(
|
||||
app_handle: &AppHandle,
|
||||
id: String,
|
||||
file_descriptions: Vec<GameFileDescription>,
|
||||
) {
|
||||
log::info!("PeerEvent::GotGameFiles received");
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-download-pre",
|
||||
&id,
|
||||
"PeerEvent::GotGameFiles",
|
||||
);
|
||||
|
||||
let state = app_handle.state::<LanSpreadState>();
|
||||
let peer_ctrl = state.peer_ctrl.read().await.clone();
|
||||
if let Some(peer_ctrl) = peer_ctrl
|
||||
&& let Err(e) = peer_ctrl.send(PeerCommand::DownloadGameFiles {
|
||||
id,
|
||||
file_descriptions,
|
||||
})
|
||||
{
|
||||
log::error!("Failed to send PeerCommand::DownloadGameFiles: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_download_finished(app_handle: &AppHandle, id: String) {
|
||||
log::info!("PeerEvent::DownloadGameFilesFinished received");
|
||||
emit_game_id_event(
|
||||
app_handle,
|
||||
"game-download-finished",
|
||||
&id,
|
||||
"PeerEvent::DownloadGameFilesFinished",
|
||||
);
|
||||
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.remove(&id);
|
||||
|
||||
let games_folder = app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.games_folder
|
||||
.read()
|
||||
.await
|
||||
.clone();
|
||||
|
||||
if let Ok(sidecar) = app_handle.shell().sidecar("unrar") {
|
||||
let app_handle = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
unpack_game(&id, sidecar, &games_folder).await;
|
||||
|
||||
if !games_folder.is_empty() {
|
||||
let backup_name = format!("___TO_BE_DELETE___{id}");
|
||||
let backup_path = PathBuf::from(&games_folder).join(backup_name);
|
||||
|
||||
if let Err(e) = cleanup_backup_folder(&backup_path) {
|
||||
log::error!("Failed to cleanup backup folder after successful update: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("PeerEvent::UnpackGameFinished received");
|
||||
emit_game_id_event(
|
||||
&app_handle,
|
||||
"game-unpack-finished",
|
||||
&id,
|
||||
"PeerEvent::UnpackGameFinished",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
@@ -831,7 +1044,7 @@ pub fn run() {
|
||||
.level_for("mdns_sd::service_daemon", log::LevelFilter::Off);
|
||||
|
||||
// channel to receive events from the peer
|
||||
let (tx_peer_event, mut rx_peer_event) = tokio::sync::mpsc::unbounded_channel::<PeerEvent>();
|
||||
let (tx_peer_event, rx_peer_event) = tokio::sync::mpsc::unbounded_channel::<PeerEvent>();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
@@ -848,232 +1061,10 @@ pub fn run() {
|
||||
get_game_thumbnail
|
||||
])
|
||||
.manage(LanSpreadState::default())
|
||||
.setup({
|
||||
let tx_peer_event_clone = tx_peer_event.clone();
|
||||
move |app| {
|
||||
// Initialize peer system ONLY when games directory is set (games directory is mandatory)
|
||||
// But the UI is responsive immediately - no blocking server discovery
|
||||
let app_handle_clone = app.handle().clone();
|
||||
let tx_peer_event_for_spawn = tx_peer_event_clone.clone();
|
||||
let peer_game_db_for_spawn = app.state::<LanSpreadState>().peer_game_db.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// Wait for games directory to be set by user (this is mandatory)
|
||||
loop {
|
||||
let games_folder = {
|
||||
let state = app_handle_clone.state::<LanSpreadState>();
|
||||
state.games_folder.read().await.clone()
|
||||
};
|
||||
|
||||
if !games_folder.is_empty() {
|
||||
let game_db = load_bundled_game_db(&app_handle_clone).await;
|
||||
{
|
||||
let state = app_handle_clone.state::<LanSpreadState>();
|
||||
*state.games.write().await = game_db;
|
||||
}
|
||||
|
||||
refresh_games_list(&app_handle_clone).await;
|
||||
|
||||
// Only start peer system when we have a valid games directory
|
||||
match start_peer(
|
||||
games_folder,
|
||||
tx_peer_event_for_spawn.clone(),
|
||||
peer_game_db_for_spawn.clone(),
|
||||
) {
|
||||
Ok(peer_ctrl) => {
|
||||
let state = app_handle_clone.state::<LanSpreadState>();
|
||||
*state.peer_ctrl.write().await = Some(peer_ctrl);
|
||||
log::info!("Peer system initialized successfully with games directory");
|
||||
|
||||
// Wait a moment for local game database to be loaded before starting discovery
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||
|
||||
// Start peer discovery and request games from other peers
|
||||
if let Err(e) = request_games(state).await {
|
||||
log::error!("Failed to request games after peer init: {e}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to initialize peer system: {e}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check every 100ms for games directory (non-blocking)
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
}
|
||||
});
|
||||
|
||||
let app_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(event) = rx_peer_event.recv().await {
|
||||
match event {
|
||||
PeerEvent::ListGames(games) => {
|
||||
log::info!("PeerEvent::ListGames received");
|
||||
update_game_db(games, app_handle.clone()).await;
|
||||
}
|
||||
PeerEvent::LocalGamesUpdated(local_games) => {
|
||||
log::info!("PeerEvent::LocalGamesUpdated received");
|
||||
update_local_games_in_db(local_games, app_handle.clone()).await;
|
||||
}
|
||||
PeerEvent::GotGameFiles { id, file_descriptions } => {
|
||||
log::info!("PeerEvent::GotGameFiles received");
|
||||
|
||||
if let Err(e) = app_handle.emit(
|
||||
"game-download-pre",
|
||||
Some(id.clone()),
|
||||
) {
|
||||
log::error!("PeerEvent::GotGameFiles: Failed to emit game-download-pre event: {e}");
|
||||
}
|
||||
|
||||
let state = app_handle.state::<LanSpreadState>();
|
||||
let peer_ctrl = state.peer_ctrl.read().await.clone();
|
||||
if let Some(peer_ctrl) = peer_ctrl
|
||||
&& let Err(e) = peer_ctrl.send(PeerCommand::DownloadGameFiles{
|
||||
id,
|
||||
file_descriptions,
|
||||
}) {
|
||||
log::error!("Failed to send PeerCommand::DownloadGameFiles: {e}");
|
||||
}
|
||||
|
||||
}
|
||||
PeerEvent::NoPeersHaveGame { id } => {
|
||||
log::warn!("PeerEvent::NoPeersHaveGame received for {id}");
|
||||
|
||||
if let Err(e) = app_handle.emit("game-no-peers", Some(id.clone())) {
|
||||
log::error!("PeerEvent::NoPeersHaveGame: Failed to emit game-no-peers event: {e}");
|
||||
}
|
||||
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.remove(&id);
|
||||
}
|
||||
PeerEvent::DownloadGameFilesBegin { id } => {
|
||||
log::info!("PeerEvent::DownloadGameFilesBegin received");
|
||||
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.insert(id.clone());
|
||||
|
||||
if let Err(e) = app_handle.emit("game-download-begin", Some(id)) {
|
||||
log::error!("PeerEvent::DownloadGameFilesBegin: Failed to emit game-download-begin event: {e}");
|
||||
}
|
||||
}
|
||||
PeerEvent::DownloadGameFilesFinished { id } => {
|
||||
log::info!("PeerEvent::DownloadGameFilesFinished received");
|
||||
if let Err(e) = app_handle.emit("game-download-finished", Some(id.clone())) {
|
||||
log::error!("PeerEvent::DownloadGameFilesFinished: Failed to emit game-download-finished event: {e}");
|
||||
}
|
||||
|
||||
app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.games_in_download
|
||||
.write()
|
||||
.await
|
||||
.remove(&id.clone());
|
||||
|
||||
let games_folder = app_handle
|
||||
.state::<LanSpreadState>()
|
||||
.inner()
|
||||
.games_folder
|
||||
.read()
|
||||
.await
|
||||
.clone();
|
||||
|
||||
if let Ok(sidecar) = app_handle.shell().sidecar("unrar") {
|
||||
|
||||
let app_handle = app_handle.clone();
|
||||
|
||||
// Spawn a separate task to handle unpacking and backup cleanup
|
||||
tauri::async_runtime::spawn(async move {
|
||||
unpack_game(&id, sidecar, &games_folder).await;
|
||||
|
||||
if !games_folder.is_empty() {
|
||||
let backup_name = format!("___TO_BE_DELETE___{id}");
|
||||
let backup_path = PathBuf::from(&games_folder).join(backup_name);
|
||||
|
||||
if let Err(e) = cleanup_backup_folder(&backup_path) {
|
||||
log::error!("Failed to cleanup backup folder after successful update: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("PeerEvent::UnpackGameFinished received");
|
||||
if let Err(e) = app_handle.emit("game-unpack-finished", Some(id.clone())) {
|
||||
log::error!("PeerEvent::UnpackGameFinished: Failed to emit game-unpack-finished event: {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
PeerEvent::DownloadGameFilesFailed { id } => {
|
||||
log::warn!("PeerEvent::DownloadGameFilesFailed received");
|
||||
|
||||
if let Err(e) = app_handle.emit("game-download-failed", Some(id.clone())) {
|
||||
log::error!("Failed to emit game-download-failed event: {e}");
|
||||
}
|
||||
|
||||
cleanup_failed_download(&app_handle, &id).await;
|
||||
}
|
||||
PeerEvent::DownloadGameFilesAllPeersGone { id } => {
|
||||
log::warn!(
|
||||
"PeerEvent::DownloadGameFilesAllPeersGone received for {id}"
|
||||
);
|
||||
|
||||
if let Err(e) = app_handle.emit(
|
||||
"game-download-peers-gone",
|
||||
Some(id.clone()),
|
||||
) {
|
||||
log::error!(
|
||||
"Failed to emit game-download-peers-gone event: {e}"
|
||||
);
|
||||
}
|
||||
|
||||
cleanup_failed_download(&app_handle, &id).await;
|
||||
}
|
||||
PeerEvent::PeerConnected(addr) => {
|
||||
log::info!("Peer connected: {addr}");
|
||||
if let Err(e) = app_handle.emit("peer-connected", Some(addr.to_string())) {
|
||||
log::error!("Failed to emit peer-connected event: {e}");
|
||||
}
|
||||
}
|
||||
PeerEvent::PeerDisconnected(addr) => {
|
||||
log::info!("Peer disconnected: {addr}");
|
||||
if let Err(e) = app_handle.emit("peer-disconnected", Some(addr.to_string())) {
|
||||
log::error!("Failed to emit peer-disconnected event: {e}");
|
||||
}
|
||||
}
|
||||
PeerEvent::PeerDiscovered(addr) => {
|
||||
log::info!("Peer discovered: {addr}");
|
||||
if let Err(e) = app_handle.emit("peer-discovered", Some(addr.to_string())) {
|
||||
log::error!("Failed to emit peer-discovered event: {e}");
|
||||
}
|
||||
}
|
||||
PeerEvent::PeerLost(addr) => {
|
||||
log::info!("Peer lost: {addr}");
|
||||
if let Err(e) = app_handle.emit("peer-lost", Some(addr.to_string())) {
|
||||
log::error!("Failed to emit peer-lost event: {e}");
|
||||
}
|
||||
}
|
||||
PeerEvent::PeerCountUpdated(count) => {
|
||||
log::info!("Peer count updated: {count}");
|
||||
if let Err(e) = app_handle.emit("peer-count-updated", Some(count)) {
|
||||
log::error!("Failed to emit peer-count-updated event: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
.manage(PeerEventTx(tx_peer_event))
|
||||
.setup(move |app| {
|
||||
spawn_peer_event_loop(app.handle().clone(), rx_peer_event);
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user