ChatGPT Codex 5.5 xhigh refactored even more
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
//! Protocol handshakes and library synchronization between peers.
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use lanspread_proto::{Hello, HelloAck, LibraryDelta, LibrarySnapshot, PROTOCOL_VERSION};
|
||||
use tokio::sync::{RwLock, mpsc::UnboundedSender};
|
||||
|
||||
use crate::{
|
||||
PeerEvent,
|
||||
context::PeerCtx,
|
||||
events,
|
||||
identity::default_features,
|
||||
library::{LocalLibraryState, build_library_snapshot, build_library_summary},
|
||||
network::{exchange_hello, send_library_delta, send_library_snapshot, send_library_summary},
|
||||
peer_db::{PeerGameDB, PeerId, PeerUpsert},
|
||||
};
|
||||
|
||||
enum LibraryUpdate {
|
||||
Delta(LibraryDelta),
|
||||
Snapshot(LibrarySnapshot),
|
||||
}
|
||||
|
||||
pub(super) async fn build_hello_ack(ctx: &PeerCtx) -> HelloAck {
|
||||
let library_guard = ctx.local_library.read().await;
|
||||
HelloAck {
|
||||
peer_id: ctx.peer_id.as_ref().clone(),
|
||||
proto_ver: PROTOCOL_VERSION,
|
||||
library_rev: library_guard.revision,
|
||||
library_digest: library_guard.digest,
|
||||
features: default_features(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn build_hello_from_state(
|
||||
peer_id: &str,
|
||||
local_library: &Arc<RwLock<LocalLibraryState>>,
|
||||
) -> Hello {
|
||||
let library_guard = local_library.read().await;
|
||||
Hello {
|
||||
peer_id: peer_id.to_string(),
|
||||
proto_ver: PROTOCOL_VERSION,
|
||||
library_rev: library_guard.revision,
|
||||
library_digest: library_guard.digest,
|
||||
features: default_features(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn perform_handshake_with_peer(
|
||||
peer_id: Arc<String>,
|
||||
local_library: Arc<RwLock<LocalLibraryState>>,
|
||||
peer_game_db: Arc<RwLock<PeerGameDB>>,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
peer_addr: SocketAddr,
|
||||
peer_id_hint: Option<PeerId>,
|
||||
) -> eyre::Result<()> {
|
||||
let hello = build_hello_from_state(peer_id.as_ref(), &local_library).await;
|
||||
let ack = exchange_hello(peer_addr, hello).await?;
|
||||
|
||||
if ack.proto_ver != PROTOCOL_VERSION {
|
||||
log::warn!(
|
||||
"Peer {peer_addr} uses incompatible protocol {} (expected {PROTOCOL_VERSION})",
|
||||
ack.proto_ver
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if ack.peer_id == *peer_id {
|
||||
log::trace!("Ignoring handshake with self for {peer_addr}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(expected) = peer_id_hint.as_ref()
|
||||
&& expected != &ack.peer_id
|
||||
{
|
||||
log::warn!(
|
||||
"Peer {peer_addr} id mismatch: mDNS advertised {expected}, hello ack returned {}",
|
||||
ack.peer_id
|
||||
);
|
||||
let _ = peer_game_db.write().await.remove_peer(expected);
|
||||
}
|
||||
|
||||
let upsert = record_remote_library(
|
||||
&peer_game_db,
|
||||
ack.peer_id.clone(),
|
||||
peer_addr,
|
||||
ack.library_rev,
|
||||
ack.library_digest,
|
||||
ack.features.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
after_peer_library_recorded(
|
||||
upsert,
|
||||
peer_addr,
|
||||
ack.library_rev,
|
||||
ack.library_digest,
|
||||
&local_library,
|
||||
&peer_game_db,
|
||||
&tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn accept_inbound_hello(
|
||||
ctx: &PeerCtx,
|
||||
remote_addr: Option<SocketAddr>,
|
||||
hello: Hello,
|
||||
) -> HelloAck {
|
||||
if hello.peer_id == *ctx.peer_id {
|
||||
log::trace!("Ignoring hello from self");
|
||||
return build_hello_ack(ctx).await;
|
||||
}
|
||||
|
||||
if hello.proto_ver != PROTOCOL_VERSION {
|
||||
log::warn!(
|
||||
"Incompatible protocol from {remote_addr:?}: {}",
|
||||
hello.proto_ver
|
||||
);
|
||||
return build_hello_ack(ctx).await;
|
||||
}
|
||||
|
||||
if let Some(addr) = remote_addr {
|
||||
let upsert = record_remote_library(
|
||||
&ctx.peer_game_db,
|
||||
hello.peer_id.clone(),
|
||||
addr,
|
||||
hello.library_rev,
|
||||
hello.library_digest,
|
||||
hello.features.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
after_peer_library_recorded(
|
||||
upsert,
|
||||
addr,
|
||||
hello.library_rev,
|
||||
hello.library_digest,
|
||||
&ctx.local_library,
|
||||
&ctx.peer_game_db,
|
||||
&ctx.tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
build_hello_ack(ctx).await
|
||||
}
|
||||
|
||||
pub(super) fn spawn_library_resync(
|
||||
peer_id: Arc<String>,
|
||||
local_library: Arc<RwLock<LocalLibraryState>>,
|
||||
peer_game_db: Arc<RwLock<PeerGameDB>>,
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
peer_addr: SocketAddr,
|
||||
peer_id_hint: PeerId,
|
||||
reason: &'static str,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = perform_handshake_with_peer(
|
||||
peer_id,
|
||||
local_library,
|
||||
peer_game_db,
|
||||
tx_notify_ui,
|
||||
peer_addr,
|
||||
Some(peer_id_hint),
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::warn!("Failed to {reason} library from {peer_addr}: {err}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn record_remote_library(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
peer_id: PeerId,
|
||||
peer_addr: SocketAddr,
|
||||
library_rev: u64,
|
||||
library_digest: u64,
|
||||
features: Vec<String>,
|
||||
) -> PeerUpsert {
|
||||
let mut db = peer_game_db.write().await;
|
||||
let upsert = db.upsert_peer(peer_id.clone(), peer_addr);
|
||||
db.update_peer_library(&peer_id, library_rev, library_digest, features);
|
||||
upsert
|
||||
}
|
||||
|
||||
async fn after_peer_library_recorded(
|
||||
upsert: PeerUpsert,
|
||||
peer_addr: SocketAddr,
|
||||
remote_library_rev: u64,
|
||||
remote_library_digest: u64,
|
||||
local_library: &Arc<RwLock<LocalLibraryState>>,
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) {
|
||||
if upsert.is_new {
|
||||
events::emit_peer_discovered(peer_game_db, tx_notify_ui, peer_addr).await;
|
||||
send_local_library_summary(peer_addr, local_library).await;
|
||||
}
|
||||
|
||||
send_local_library_update_if_needed(
|
||||
peer_addr,
|
||||
local_library,
|
||||
remote_library_rev,
|
||||
remote_library_digest,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn send_local_library_summary(
|
||||
peer_addr: SocketAddr,
|
||||
local_library: &Arc<RwLock<LocalLibraryState>>,
|
||||
) {
|
||||
let summary = {
|
||||
let library_guard = local_library.read().await;
|
||||
build_library_summary(&library_guard)
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = send_library_summary(peer_addr, summary).await {
|
||||
log::warn!("Failed to send library summary to {peer_addr}: {err}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn send_local_library_update_if_needed(
|
||||
peer_addr: SocketAddr,
|
||||
local_library: &Arc<RwLock<LocalLibraryState>>,
|
||||
remote_rev: u64,
|
||||
remote_digest: u64,
|
||||
) {
|
||||
if let Some(update) = select_library_update(local_library, remote_rev, remote_digest).await {
|
||||
tokio::spawn(async move {
|
||||
let result = match update {
|
||||
LibraryUpdate::Delta(delta) => send_library_delta(peer_addr, delta).await,
|
||||
LibraryUpdate::Snapshot(snapshot) => {
|
||||
send_library_snapshot(peer_addr, snapshot).await
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
log::warn!("Failed to send library update to {peer_addr}: {err}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn select_library_update(
|
||||
local_library: &Arc<RwLock<LocalLibraryState>>,
|
||||
remote_rev: u64,
|
||||
remote_digest: u64,
|
||||
) -> Option<LibraryUpdate> {
|
||||
let library_guard = local_library.read().await;
|
||||
if library_guard.digest == remote_digest || remote_rev > library_guard.revision {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(delta) = library_guard.delta_since(remote_rev) {
|
||||
return Some(LibraryUpdate::Delta(delta));
|
||||
}
|
||||
|
||||
Some(LibraryUpdate::Snapshot(build_library_snapshot(
|
||||
&library_guard,
|
||||
)))
|
||||
}
|
||||
Reference in New Issue
Block a user