diff --git a/other_crates/bones_matchmaker/src/lib.rs b/other_crates/bones_matchmaker/src/lib.rs index 483d3459c5..d53ac69bb0 100644 --- a/other_crates/bones_matchmaker/src/lib.rs +++ b/other_crates/bones_matchmaker/src/lib.rs @@ -6,9 +6,11 @@ extern crate tracing; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::sync::Arc; use bones_matchmaker_proto::MATCH_ALPN; use iroh::key::SecretKey; +use matchmaker::Matchmaker; pub mod cli; mod helpers; @@ -74,24 +76,16 @@ async fn server(args: Config) -> anyhow::Result<()> { println!("Node ID: {}", my_addr.node_id); - // Listen for incomming connections - while let Some(connecting) = endpoint.accept().await { - let connection = connecting.await; - - match connection { - Ok(conn) => { - info!( - connection_id = conn.stable_id(), - addr = ?conn.remote_address(), - "Accepted connection from client" - ); - - // Spawn a task to handle the new connection - tokio::task::spawn(matchmaker::handle_connection(endpoint.clone(), conn)); - } - Err(e) => error!("Error opening client connection: {e:?}"), - } - } + let matchmaker = Matchmaker::new(endpoint.clone()); + let router = iroh::protocol::Router::builder(endpoint) + .accept(MATCH_ALPN, Arc::new(matchmaker)) + .spawn() + .await?; + + // wait for shutdown + tokio::signal::ctrl_c().await?; + + router.shutdown().await?; info!("Server shutdown"); diff --git a/other_crates/bones_matchmaker/src/lobbies.rs b/other_crates/bones_matchmaker/src/lobbies.rs index 7bb3015437..309af834d6 100644 --- a/other_crates/bones_matchmaker/src/lobbies.rs +++ b/other_crates/bones_matchmaker/src/lobbies.rs @@ -88,7 +88,7 @@ pub async fn handle_create_lobby( /// Handles a request to join an existing lobby pub async fn handle_join_lobby( - ep: Endpoint, + ep: &Endpoint, conn: Connection, game_id: GameID, lobby_id: LobbyId, @@ -169,6 +169,7 @@ pub async fn handle_join_lobby( { let members = connections.1; drop(state); + let ep = ep.clone(); tokio::spawn(async move { if let Err(e) = start_game(ep, members, &match_info).await { error!("Error starting match from full lobby: {:?}", e); diff --git a/other_crates/bones_matchmaker/src/matchmaker.rs b/other_crates/bones_matchmaker/src/matchmaker.rs index f8af301406..979e5c720d 100644 --- a/other_crates/bones_matchmaker/src/matchmaker.rs +++ b/other_crates/bones_matchmaker/src/matchmaker.rs @@ -6,6 +6,7 @@ use bones_matchmaker_proto::{ GameID, LobbyId, LobbyInfo, MatchInfo, MatchmakerRequest, MatchmakerResponse, PlayerIdxAssignment, }; +use futures::future::BoxFuture; use iroh::{endpoint::Connection, Endpoint, NodeAddr}; use once_cell::sync::Lazy; use rand::{prelude::SliceRandom, SeedableRng}; @@ -34,38 +35,73 @@ pub struct State { pub static MATCHMAKER_STATE: Lazy>> = Lazy::new(|| Arc::new(Mutex::new(State::default()))); -/// Handles incoming connections and routes requests to appropriate handlers -pub async fn handle_connection(ep: Endpoint, conn: Connection) -> Result<()> { - let connection_id = conn.stable_id(); - loop { - tokio::select! { - _ = conn.closed() => { - info!("[{}] Client closed connection.", connection_id); - return Ok(()); +#[derive(Debug)] +pub struct Matchmaker { + endpoint: Endpoint, +} + +impl iroh::protocol::ProtocolHandler for Matchmaker { + fn accept(self: Arc, conn: iroh::endpoint::Connecting) -> BoxFuture<'static, Result<()>> { + Box::pin(async move { + let connection = conn.await; + + match connection { + Ok(conn) => { + info!( + connection_id = conn.stable_id(), + addr = ?conn.remote_address(), + "Accepted connection from client" + ); + + // Spawn a task to handle the new connection + self.handle_connection(conn).await?; + } + Err(e) => error!("Error opening client connection: {e:?}"), } - bi = conn.accept_bi() => { - let (mut send, mut recv) = bi?; - // Parse the incoming request - let request: MatchmakerRequest = postcard::from_bytes(&recv.read_to_end(256).await?)?; - - // Route the request to the appropriate handler - match request { - MatchmakerRequest::RequestMatchmaking(match_info) => { - handle_request_matchaking(ep.clone(), conn.clone(), match_info, &mut send).await?; - send.finish()?; - send.stopped().await?; - } - MatchmakerRequest::StopMatchmaking(match_info) => { - handle_stop_matchmaking(conn.clone(), match_info, &mut send).await?; - } - MatchmakerRequest::ListLobbies(game_id) => { - handle_list_lobbies(game_id, &mut send).await?; - } - MatchmakerRequest::CreateLobby(lobby_info) => { - handle_create_lobby(conn.clone(), lobby_info, &mut send).await?; - } - MatchmakerRequest::JoinLobby(game_id, lobby_id, password) => { - handle_join_lobby(ep.clone(), conn.clone(), game_id, lobby_id, password, &mut send).await?; + + Ok(()) + }) + } +} + +impl Matchmaker { + pub fn new(endpoint: Endpoint) -> Self { + Matchmaker { endpoint } + } + + /// Handles incoming connections and routes requests to appropriate handlers + async fn handle_connection(&self, conn: Connection) -> Result<()> { + let connection_id = conn.stable_id(); + loop { + tokio::select! { + _ = conn.closed() => { + info!("[{}] Client closed connection.", connection_id); + return Ok(()); + } + bi = conn.accept_bi() => { + let (mut send, mut recv) = bi?; + // Parse the incoming request + let request: MatchmakerRequest = postcard::from_bytes(&recv.read_to_end(256).await?)?; + + // Route the request to the appropriate handler + match request { + MatchmakerRequest::RequestMatchmaking(match_info) => { + handle_request_matchaking(&self.endpoint, conn.clone(), match_info, &mut send).await?; + send.finish()?; + send.stopped().await?; + } + MatchmakerRequest::StopMatchmaking(match_info) => { + handle_stop_matchmaking(conn.clone(), match_info, &mut send).await?; + } + MatchmakerRequest::ListLobbies(game_id) => { + handle_list_lobbies(game_id, &mut send).await?; + } + MatchmakerRequest::CreateLobby(lobby_info) => { + handle_create_lobby(conn.clone(), lobby_info, &mut send).await?; + } + MatchmakerRequest::JoinLobby(game_id, lobby_id, password) => { + handle_join_lobby(&self.endpoint, conn.clone(), game_id, lobby_id, password, &mut send).await?; + } } } } diff --git a/other_crates/bones_matchmaker/src/matchmaking.rs b/other_crates/bones_matchmaker/src/matchmaking.rs index b1a854ad91..34253140c7 100644 --- a/other_crates/bones_matchmaker/src/matchmaking.rs +++ b/other_crates/bones_matchmaker/src/matchmaking.rs @@ -67,7 +67,7 @@ pub async fn handle_stop_matchmaking( /// Handles a start matchmaking request from a client pub async fn handle_request_matchaking( - ep: Endpoint, + ep: &Endpoint, conn: Connection, match_info: MatchInfo, send: &mut iroh::endpoint::SendStream, @@ -267,7 +267,7 @@ async fn send_matchmaking_updates( } /// Starts a matchmade game if the room is ready with sufficient players -async fn start_matchmaked_game_if_ready(ep: Endpoint, match_info: &MatchInfo) -> Result<()> { +async fn start_matchmaked_game_if_ready(ep: &Endpoint, match_info: &MatchInfo) -> Result<()> { let members = { let state = MATCHMAKER_STATE.lock().await; state @@ -279,6 +279,7 @@ async fn start_matchmaked_game_if_ready(ep: Endpoint, match_info: &MatchInfo) -> if let Some(members) = members { let cloned_match_info = match_info.clone(); let players_len = members.len(); + let ep = ep.clone(); tokio::spawn(async move { match start_game(ep, members, &cloned_match_info).await { Ok(_) => info!("Starting matchmaked game with {} players", players_len),