diff --git a/packages/app-lib/src/api/worlds.rs b/packages/app-lib/src/api/worlds.rs index e731a221..0f274876 100644 --- a/packages/app-lib/src/api/worlds.rs +++ b/packages/app-lib/src/api/worlds.rs @@ -26,6 +26,8 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use std::path::{Path, PathBuf}; use std::sync::LazyLock; use tokio::io::AsyncWriteExt; +use tokio::sync::Semaphore; +use tokio::task::JoinSet; use tokio_util::compat::FuturesAsyncWriteCompatExt; use url::Url; @@ -394,25 +396,27 @@ async fn get_server_worlds_in_profile( .await .ok(); + let first_server_index = worlds.len(); for (index, server) in servers.into_iter().enumerate() { if server.hidden { // TODO: Figure out whether we want to hide or show direct connect servers continue; } - let icon = server.icon.and_then(|icon| { - Url::parse(&format!("data:image/png;base64,{icon}")).ok() - }); - let last_played = join_log - .as_ref() - .and_then(|log| { - let address = parse_server_address(&server.ip).ok()?; - log.get(&(address.0.to_owned(), address.1)) - }) - .copied(); let world = World { name: server.name, - last_played, - icon: icon.map(Either::Right), + last_played: join_log + .as_ref() + .and_then(|log| { + let (host, port) = parse_server_address(&server.ip).ok()?; + log.get(&(host.to_owned(), port)) + }) + .copied(), + icon: server + .icon + .and_then(|icon| { + Url::parse(&format!("data:image/png;base64,{icon}")).ok() + }) + .map(Either::Right), display_status: DisplayStatus::Normal, details: WorldDetails::Server { index, @@ -423,6 +427,30 @@ async fn get_server_worlds_in_profile( worlds.push(world); } + if let Some(join_log) = join_log { + let mut futures = JoinSet::new(); + for (index, world) in worlds.iter().enumerate().skip(first_server_index) + { + if world.last_played.is_some() { + continue; + } + if let WorldDetails::Server { address, .. } = &world.details + && let Ok((host, port)) = parse_server_address(address) + { + let host = host.to_owned(); + futures.spawn(async move { + resolve_server_address(&host, port) + .await + .ok() + .map(|x| (index, x)) + }); + } + } + for (index, address) in futures.join_all().await.into_iter().flatten() { + worlds[index].last_played = join_log.get(&address).copied(); + } + } + Ok(()) } @@ -943,9 +971,13 @@ async fn resolve_server_address( host: &str, port: u16, ) -> Result<(String, u16)> { + static SIMULTANEOUS_DNS_QUERIES: Semaphore = Semaphore::const_new(24); + if host.parse::().is_ok() || host.parse::().is_ok() { return Ok((host.to_owned(), port)); } + + let _permit = SIMULTANEOUS_DNS_QUERIES.acquire().await?; let resolver = hickory_resolver::TokioResolver::builder_tokio()?.build(); Ok( match resolver.srv_lookup(format!("_minecraft._tcp.{host}")).await {