ChatGPT Codex 5.5 xhigh refactored even more
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
//! mDNS peer discovery and discovery-time protocol negotiation.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use lanspread_mdns::{LANSPREAD_SERVICE_TYPE, MdnsBrowser, MdnsService};
|
||||
use lanspread_proto::PROTOCOL_VERSION;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use crate::{
|
||||
PeerEvent,
|
||||
context::Ctx,
|
||||
events,
|
||||
peer_db::PeerId,
|
||||
services::{handshake::perform_handshake_with_peer, legacy::request_games_from_peer},
|
||||
};
|
||||
|
||||
struct MdnsPeerInfo {
|
||||
addr: std::net::SocketAddr,
|
||||
peer_id: Option<PeerId>,
|
||||
proto_ver: Option<u32>,
|
||||
library_rev: u64,
|
||||
library_digest: u64,
|
||||
}
|
||||
|
||||
/// Runs the peer discovery service using mDNS.
|
||||
pub async fn run_peer_discovery(tx_notify_ui: UnboundedSender<PeerEvent>, ctx: Ctx) {
|
||||
log::info!("Starting peer discovery task");
|
||||
|
||||
let service_type = LANSPREAD_SERVICE_TYPE.to_string();
|
||||
|
||||
loop {
|
||||
let (service_tx, mut service_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let service_type_clone = service_type.clone();
|
||||
|
||||
let worker_handle = tokio::task::spawn_blocking(move || -> eyre::Result<()> {
|
||||
let browser = MdnsBrowser::new(&service_type_clone)?;
|
||||
loop {
|
||||
if let Some(service) = browser.next_service(None)? {
|
||||
if service_tx.send(service).is_err() {
|
||||
log::debug!("Peer discovery consumer dropped; stopping worker");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log::warn!("mDNS browser closed; stopping peer discovery worker");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
while let Some(service) = service_rx.recv().await {
|
||||
let info = parse_mdns_peer(&service);
|
||||
if is_self_advertisement(&info, &ctx).await {
|
||||
log::trace!("Ignoring self advertisement at {}", info.addr);
|
||||
continue;
|
||||
}
|
||||
|
||||
handle_discovered_peer(info, &ctx, &tx_notify_ui).await;
|
||||
}
|
||||
|
||||
match worker_handle.await {
|
||||
Ok(Ok(())) => {
|
||||
log::warn!("Peer discovery worker exited; restarting shortly");
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
log::error!("Peer discovery worker failed: {err}");
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Peer discovery worker join error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_mdns_peer(service: &MdnsService) -> MdnsPeerInfo {
|
||||
MdnsPeerInfo {
|
||||
addr: service.addr,
|
||||
peer_id: service.properties.get("peer_id").cloned(),
|
||||
proto_ver: service
|
||||
.properties
|
||||
.get("proto_ver")
|
||||
.and_then(|value| value.parse::<u32>().ok()),
|
||||
library_rev: service
|
||||
.properties
|
||||
.get("library_rev")
|
||||
.and_then(|value| value.parse::<u64>().ok())
|
||||
.unwrap_or(0),
|
||||
library_digest: service
|
||||
.properties
|
||||
.get("library_digest")
|
||||
.and_then(|value| value.parse::<u64>().ok())
|
||||
.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_self_advertisement(info: &MdnsPeerInfo, ctx: &Ctx) -> bool {
|
||||
let guard = ctx.local_peer_addr.read().await;
|
||||
guard.as_ref().is_some_and(|addr| *addr == info.addr)
|
||||
|| info
|
||||
.peer_id
|
||||
.as_ref()
|
||||
.is_some_and(|peer_id| peer_id == ctx.peer_id.as_ref())
|
||||
}
|
||||
|
||||
async fn handle_discovered_peer(
|
||||
info: MdnsPeerInfo,
|
||||
ctx: &Ctx,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) {
|
||||
let peer_id = info
|
||||
.peer_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("legacy-{}", info.addr));
|
||||
|
||||
let upsert = {
|
||||
let mut db = ctx.peer_game_db.write().await;
|
||||
let upsert = db.upsert_peer(peer_id.clone(), info.addr);
|
||||
let features = db.peer_features(&peer_id);
|
||||
if info.library_rev > 0 || info.library_digest > 0 {
|
||||
db.update_peer_library(&peer_id, info.library_rev, info.library_digest, features);
|
||||
}
|
||||
upsert
|
||||
};
|
||||
|
||||
if upsert.is_new {
|
||||
log::info!("Discovered peer at: {}", info.addr);
|
||||
events::emit_peer_discovered(&ctx.peer_game_db, tx_notify_ui, info.addr).await;
|
||||
}
|
||||
|
||||
if upsert.is_new || upsert.addr_changed {
|
||||
spawn_protocol_negotiation(&info, ctx, tx_notify_ui.clone(), peer_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_protocol_negotiation(
|
||||
info: &MdnsPeerInfo,
|
||||
ctx: &Ctx,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
peer_id: PeerId,
|
||||
) {
|
||||
let peer_addr = info.addr;
|
||||
let proto_ver = info.proto_ver;
|
||||
let peer_id_arc = ctx.peer_id.clone();
|
||||
let local_library = ctx.local_library.clone();
|
||||
let peer_game_db = ctx.peer_game_db.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let handshake_result = if proto_ver.is_none() || proto_ver == Some(PROTOCOL_VERSION) {
|
||||
perform_handshake_with_peer(
|
||||
peer_id_arc,
|
||||
local_library,
|
||||
peer_game_db.clone(),
|
||||
tx_notify_ui.clone(),
|
||||
peer_addr,
|
||||
Some(peer_id),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
Err(eyre::eyre!("Skipping hello for legacy peer"))
|
||||
};
|
||||
|
||||
if handshake_result.is_err()
|
||||
&& let Err(err) = request_games_from_peer(peer_addr, tx_notify_ui, peer_game_db).await
|
||||
{
|
||||
log::error!("Failed to request games from peer {peer_addr}: {err}");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user