aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-11-21 22:53:38 +0100
committermat <git@matdoes.dev>2025-11-21 22:53:38 +0100
commit6930966aabf9b49fb6a0dc8b61076fa3f1abc298 (patch)
tree52bd3e48c96f0898d0dce494044fbb40915da73f
parentf464f0152a6450fafc86e1dc993b90e12e395fe7 (diff)
downloadazalea-drasl-6930966aabf9b49fb6a0dc8b61076fa3f1abc298.tar.xz
refactor resolve_address
-rw-r--r--CHANGELOG.md5
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml1
-rw-r--r--azalea-client/src/client.rs10
-rw-r--r--azalea-client/src/ping.rs8
-rw-r--r--azalea-protocol/Cargo.toml1
-rw-r--r--azalea-protocol/src/lib.rs7
-rw-r--r--azalea-protocol/src/resolve.rs69
-rw-r--r--azalea-protocol/src/resolver.rs74
-rw-r--r--azalea/src/lib.rs4
-rw-r--r--azalea/src/swarm/mod.rs4
11 files changed, 92 insertions, 103 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03459fc9..ad88fbf5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,20 +23,23 @@ is breaking anyways, semantic versioning is not followed.
- Swap the order of the type parameters in entity filtering functions so query is first, then filter.
- Add optional `timeout_ticks` field to `Client::open_container_at`.
- Rename `ResourceLocation` to `Identifier` to match Minecraft's new internal naming.
+- Rename `azalea_protocol::resolver` to `resolve` and `ResolverError` to `ResolveError`.
### Fixed
- The wrong path was temporarily executed if we received a `GotoEvent` while the path that's being executed was more than 50 nodes long.
- The pathfinder can now jump from dirt path and farmland blocks correctly.
- Don't panic when receiving an unexpected `PathFoundEvent`. (@Hiradpi)
-- Don't panic when the `LocalPlayerEvents` component is missing. (@suprohub)
- The pathfinder sometimes got stuck when going up stairs that are facing the wrong direction.
- ReachBlockPosGoal had the wrong cost when the destination is surrounded in blocks.
- Some parkour movements had the wrong costs.
- The pathfinder no longer spins when descending more than one block.
- The pathfinder now avoids slipping off when the last block of the path is on ice.
+- Don't panic when the `LocalPlayerEvents` component is missing. (@suprohub)
- The 'with' field in formatted text didn't correctly support mixed types. (@Tert0)
- The WritableBookContent and ResolvableProfile data components had the wrong protocol implementations.
+- Resolving server addresses shouldn't be recursive.
+
## [0.14.0+mc1.21.8] - 2025-09-28
diff --git a/Cargo.lock b/Cargo.lock
index 0892d4ae..de403e1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -162,17 +162,6 @@ dependencies = [
]
[[package]]
-name = "async-recursion"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -490,7 +479,6 @@ name = "azalea-protocol"
version = "0.14.0+mc1.21.10"
dependencies = [
"anyhow",
- "async-recursion",
"azalea-auth",
"azalea-block",
"azalea-brigadier",
diff --git a/Cargo.toml b/Cargo.toml
index e39de075..ec8c3136 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,7 +32,6 @@ repository = "https://github.com/azalea-rs/azalea"
aes = "0.8.4"
anyhow = "1.0.100"
async-compat = "0.2.5"
-async-recursion = "1.1.1"
base64 = "0.22.1"
bevy_app = "0.17.2"
bevy_ecs = { version = "0.17.2", default-features = false }
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index e0902779..20e1cb3b 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -23,7 +23,7 @@ use azalea_protocol::{
ServerAddress,
connect::Proxy,
packets::{Packet, game::ServerboundGamePacket},
- resolver,
+ resolve,
};
use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
use bevy_app::{App, AppExit, Plugin, PluginsState, SubApp, Update};
@@ -88,8 +88,8 @@ pub struct Client {
/// An error that happened while joining the server.
#[derive(Error, Debug)]
pub enum JoinError {
- #[error("{0}")]
- Resolver(#[from] resolver::ResolverError),
+ #[error(transparent)]
+ Resolver(#[from] resolve::ResolveError),
#[error("The given address could not be parsed into a ServerAddress")]
InvalidAddress,
}
@@ -173,7 +173,7 @@ impl Client {
address: impl TryInto<ServerAddress>,
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
- let resolved_address = resolver::resolve_address(&address).await?;
+ let resolved_address = resolve::resolve_address(&address).await?;
let (tx, rx) = mpsc::unbounded_channel();
let client = Self::start_client(StartClientOpts::new(
@@ -192,7 +192,7 @@ impl Client {
proxy: Proxy,
) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
- let resolved_address = resolver::resolve_address(&address).await?;
+ let resolved_address = resolve::resolve_address(&address).await?;
let (tx, rx) = mpsc::unbounded_channel();
let client = Self::start_client(
diff --git a/azalea-client/src/ping.rs b/azalea-client/src/ping.rs
index 8c4e2240..93018a82 100644
--- a/azalea-client/src/ping.rs
+++ b/azalea-client/src/ping.rs
@@ -16,14 +16,14 @@ use azalea_protocol::{
s_status_request::ServerboundStatusRequest,
},
},
- resolver,
+ resolve,
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum PingError {
#[error("{0}")]
- Resolver(#[from] resolver::ResolverError),
+ Resolver(#[from] resolve::ResolveError),
#[error("{0}")]
Connection(#[from] ConnectionError),
#[error("{0}")]
@@ -51,7 +51,7 @@ pub async fn ping_server(
address: impl TryInto<ServerAddress>,
) -> Result<ClientboundStatusResponse, PingError> {
let address: ServerAddress = address.try_into().map_err(|_| PingError::InvalidAddress)?;
- let resolved_address = resolver::resolve_address(&address).await?;
+ let resolved_address = resolve::resolve_address(&address).await?;
let conn = Connection::new(&resolved_address).await?;
ping_server_with_connection(address, conn).await
}
@@ -62,7 +62,7 @@ pub async fn ping_server_with_proxy(
proxy: Proxy,
) -> Result<ClientboundStatusResponse, PingError> {
let address: ServerAddress = address.try_into().map_err(|_| PingError::InvalidAddress)?;
- let resolved_address = resolver::resolve_address(&address).await?;
+ let resolved_address = resolve::resolve_address(&address).await?;
let conn = Connection::new_with_proxy(&resolved_address, proxy).await?;
ping_server_with_connection(address, conn).await
}
diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml
index df3aa615..1ab25a12 100644
--- a/azalea-protocol/Cargo.toml
+++ b/azalea-protocol/Cargo.toml
@@ -12,7 +12,6 @@ tracing.workspace = true
tracing-subscriber.workspace = true
[dependencies]
-async-recursion.workspace = true
azalea-auth.workspace = true
azalea-block.workspace = true
azalea-brigadier = { workspace = true, features = ["azalea-buf"] }
diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs
index fdba34bc..45abf240 100644
--- a/azalea-protocol/src/lib.rs
+++ b/azalea-protocol/src/lib.rs
@@ -24,9 +24,14 @@ pub mod connect;
#[cfg(feature = "packets")]
pub mod packets;
pub mod read;
-pub mod resolver;
+pub mod resolve;
pub mod write;
+#[deprecated(note = "Renamed to resolve")]
+pub mod resolver {
+ pub use super::resolve::*;
+}
+
/// A host and port. It's possible that the port doesn't resolve to anything.
///
/// # Examples
diff --git a/azalea-protocol/src/resolve.rs b/azalea-protocol/src/resolve.rs
new file mode 100644
index 00000000..f18a04a8
--- /dev/null
+++ b/azalea-protocol/src/resolve.rs
@@ -0,0 +1,69 @@
+//! Resolve a Minecraft server address into an IP address and port.
+
+use std::{
+ net::{IpAddr, SocketAddr},
+ sync::LazyLock,
+};
+
+pub use hickory_resolver::ResolveError;
+use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider};
+
+use crate::ServerAddress;
+
+#[deprecated(note = "Renamed to ResolveError")]
+pub type ResolverError = ResolveError;
+
+static RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
+ TokioResolver::builder(TokioConnectionProvider::default())
+ .unwrap()
+ .build()
+});
+
+/// Resolve a Minecraft server address into an IP address and port.
+///
+/// If it's already an IP address, it's returned as-is.
+pub async fn resolve_address(mut address: &ServerAddress) -> Result<SocketAddr, ResolveError> {
+ let redirect = resolve_srv_redirect(address).await;
+ if let Ok(redirect_target) = &redirect {
+ address = redirect_target;
+ }
+
+ resolve_ip_without_redirects(address).await
+}
+
+async fn resolve_ip_without_redirects(address: &ServerAddress) -> Result<SocketAddr, ResolveError> {
+ if let Ok(ip) = address.host.parse::<IpAddr>() {
+ // no need to do a lookup
+ return Ok(SocketAddr::new(ip, address.port));
+ }
+
+ let name = Name::from_ascii(&address.host)?;
+ let lookup_ip = RESOLVER.lookup_ip(name).await?;
+
+ let ip = lookup_ip
+ .iter()
+ .next()
+ .ok_or(hickory_resolver::ResolveError::from(
+ "No A/AAAA record found",
+ ))?;
+
+ Ok(SocketAddr::new(ip, address.port))
+}
+
+async fn resolve_srv_redirect(address: &ServerAddress) -> Result<ServerAddress, ResolveError> {
+ if address.port != 25565 {
+ return Err(ResolveError::from("Port must be 25565 to do a SRV lookup"));
+ }
+
+ let query = format!("_minecraft._tcp.{}", address.host);
+ let res = RESOLVER.srv_lookup(query).await?;
+
+ let srv = res
+ .iter()
+ .next()
+ .ok_or(ResolveError::from("No SRV record found"))?;
+ Ok(ServerAddress {
+ host: srv.target().to_ascii(),
+ port: srv.port(),
+ })
+}
diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs
deleted file mode 100644
index 4cd2faad..00000000
--- a/azalea-protocol/src/resolver.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-//! Resolve IPs from hostnames.
-
-use std::net::{IpAddr, SocketAddr};
-
-use async_recursion::async_recursion;
-use hickory_resolver::{Name, TokioResolver, name_server::TokioConnectionProvider};
-use thiserror::Error;
-
-use crate::ServerAddress;
-
-#[derive(Error, Debug)]
-pub enum ResolverError {
- #[error("No SRV record found")]
- NoSrvRecord,
- #[error("No IP found")]
- NoIp,
-}
-
-/// Resolve a Minecraft server address into an IP address and port.
-/// If it's already an IP address, it's returned as-is.
-#[must_use]
-#[async_recursion]
-pub async fn resolve_address(address: &ServerAddress) -> Result<SocketAddr, ResolverError> {
- // If the address.host is already in the format of an ip address, return it.
- if let Ok(ip) = address.host.parse::<IpAddr>() {
- return Ok(SocketAddr::new(ip, address.port));
- }
-
- // we specify Cloudflare instead of the default resolver because
- // hickory_resolver has an issue on Windows where it's really slow using the
- // default resolver
- let resolver = TokioResolver::builder(TokioConnectionProvider::default())
- .unwrap()
- .build();
-
- // first, we do a srv lookup for _minecraft._tcp.<host>
- let srv_redirect_result = resolver
- .srv_lookup(format!("_minecraft._tcp.{}", address.host).as_str())
- .await;
-
- // if it resolves that means it's a redirect so we call resolve_address again
- // with the new host
- if let Ok(redirect_result) = srv_redirect_result {
- let redirect_srv = redirect_result
- .iter()
- .next()
- .ok_or(ResolverError::NoSrvRecord)?;
- let redirect_address = ServerAddress {
- host: redirect_srv.target().to_ascii(),
- port: redirect_srv.port(),
- };
-
- if redirect_address.host == address.host {
- let lookup_ip_result = resolver.lookup_ip(redirect_address.host).await;
- let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
- return Ok(SocketAddr::new(
- lookup_ip.iter().next().unwrap(),
- redirect_address.port,
- ));
- }
-
- return resolve_address(&redirect_address).await;
- }
-
- // there's no redirect, try to resolve this as an ip address
- let name = Name::from_ascii(&address.host).map_err(|_| ResolverError::NoIp)?;
- let lookup_ip_result = resolver.lookup_ip(name).await;
- let lookup_ip = lookup_ip_result.map_err(|_| ResolverError::NoIp)?;
-
- Ok(SocketAddr::new(
- lookup_ip.iter().next().unwrap(),
- address.port,
- ))
-}
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index f4f0844d..0fec9fc0 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -39,7 +39,7 @@ use bevy_app::AppExit;
pub use bevy_ecs as ecs;
use ecs::component::Component;
use futures::{Future, future::BoxFuture};
-use protocol::{ServerAddress, connect::Proxy, resolver::ResolverError};
+use protocol::{ServerAddress, connect::Proxy, resolve::ResolveError};
use swarm::SwarmBuilder;
use thiserror::Error;
@@ -55,7 +55,7 @@ pub enum StartError {
#[error("Invalid address")]
InvalidAddress,
#[error(transparent)]
- ResolveAddress(#[from] ResolverError),
+ ResolveAddress(#[from] ResolveError),
}
/// A builder for creating new [`Client`]s. This is the recommended way of
diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs
index 34247453..d49a5190 100644
--- a/azalea/src/swarm/mod.rs
+++ b/azalea/src/swarm/mod.rs
@@ -26,7 +26,7 @@ use azalea_client::{
start_ecs_runner,
};
use azalea_entity::LocalEntity;
-use azalea_protocol::{ServerAddress, resolver};
+use azalea_protocol::{ServerAddress, resolve};
use azalea_world::InstanceContainer;
use bevy_app::{App, AppExit, PluginGroup, PluginGroupBuilder, Plugins, SubApp};
use bevy_ecs::prelude::*;
@@ -445,7 +445,7 @@ where
let resolved_address = if let Some(a) = default_join_opts.custom_resolved_address {
a
} else {
- resolver::resolve_address(&address).await?
+ resolve::resolve_address(&address).await?
};
let instance_container = Arc::new(RwLock::new(InstanceContainer::default()));