diff options
Diffstat (limited to 'azalea/src')
| -rw-r--r-- | azalea/src/lib.rs | 67 | ||||
| -rw-r--r-- | azalea/src/prelude.rs | 1 | ||||
| -rw-r--r-- | azalea/src/swarm/mod.rs | 117 |
3 files changed, 98 insertions, 87 deletions
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 8440e31f..45c0114d 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -37,6 +37,7 @@ pub use azalea_core::position::{BlockPos, Vec3}; pub use azalea_entity as entity; pub use azalea_physics as physics; pub use azalea_protocol as protocol; +use azalea_protocol::address::{ResolvableAddr, ServerAddr}; pub use azalea_registry as registry; #[doc(hidden)] #[deprecated(note = "renamed to `Identifier`.")] @@ -48,9 +49,8 @@ use bevy_app::AppExit; pub use bevy_ecs as ecs; use ecs::component::Component; use futures::{Future, future::BoxFuture}; -use protocol::{ServerAddress, connect::Proxy, resolve::ResolveError}; +use protocol::connect::Proxy; use swarm::SwarmBuilder; -use thiserror::Error; use crate::bot::DefaultBotPlugins; @@ -58,15 +58,6 @@ pub type BoxHandleFn<S, R> = Box<dyn Fn(Client, azalea_client::Event, S) -> BoxFuture<'static, R> + Send>; pub type HandleFn<S, Fut> = fn(Client, azalea_client::Event, S) -> Fut; -/// An error related to resolving the server address when starting a client. -#[derive(Error, Debug)] -pub enum StartError { - #[error("Invalid address")] - InvalidAddress, - #[error(transparent)] - ResolveAddress(#[from] ResolveError), -} - /// A builder for creating new [`Client`]s. This is the recommended way of /// making a bot. /// @@ -217,20 +208,17 @@ where /// /// If the client can't join, it'll keep retrying forever until it can. /// - /// The `address` argument can be a `&str`, [`ServerAddress`], or anything - /// that implements `TryInto<ServerAddress>`. + /// The `address` argument can be a `&str`, [`ServerAddr`], + /// [`ResolvedAddr`], or anything else that implements [`ResolvableAddr`]. /// /// # Errors /// /// This will error if the given address is invalid or couldn't be resolved /// to a Minecraft server. /// - /// [`ServerAddress`]: azalea_protocol::ServerAddress - pub async fn start( - mut self, - account: Account, - address: impl TryInto<ServerAddress>, - ) -> Result<AppExit, StartError> { + /// [`ServerAddr`]: azalea_protocol::address::ServerAddr + /// [`ResolvedAddr`]: azalea_protocol::address::ResolvedAddr + pub async fn start(mut self, account: Account, address: impl ResolvableAddr) -> AppExit { self.swarm.accounts = vec![(account, JoinOpts::default())]; if self.swarm.states.is_empty() { self.swarm.states = vec![S::default()]; @@ -243,14 +231,14 @@ where pub async fn start_with_opts( mut self, account: Account, - address: impl TryInto<ServerAddress>, + address: impl ResolvableAddr, opts: JoinOpts, - ) -> Result<AppExit, StartError> { + ) -> AppExit { self.swarm.accounts = vec![(account, opts.clone())]; if self.swarm.states.is_empty() { self.swarm.states = vec![S::default()]; } - self.swarm.start_with_default_opts(address, opts).await + self.swarm.start_with_opts(address, opts).await } } impl Default for ClientBuilder<NoState, ()> { @@ -287,10 +275,12 @@ pub struct JoinOpts { pub sessionserver_proxy: Option<Proxy>, /// Override the server address that this specific bot will send in the /// handshake packet. - pub custom_address: Option<ServerAddress>, - /// Override the socket address that this specific bot will use to connect + #[doc(alias = "custom_address")] + pub custom_server_addr: Option<ServerAddr>, + /// Override the IP and port that this specific bot will use to connect /// to the server. - pub custom_resolved_address: Option<SocketAddr>, + #[doc(alias = "custom_resolved_address")] + pub custom_socket_addr: Option<SocketAddr>, } impl JoinOpts { @@ -305,11 +295,11 @@ impl JoinOpts { if let Some(proxy) = other.sessionserver_proxy.clone() { self.sessionserver_proxy = Some(proxy); } - if let Some(custom_address) = other.custom_address.clone() { - self.custom_address = Some(custom_address); + if let Some(custom_server_addr) = other.custom_server_addr.clone() { + self.custom_server_addr = Some(custom_server_addr); } - if let Some(custom_resolved_address) = other.custom_resolved_address { - self.custom_resolved_address = Some(custom_resolved_address); + if let Some(custom_socket_addr) = other.custom_socket_addr { + self.custom_socket_addr = Some(custom_socket_addr); } } @@ -347,15 +337,26 @@ impl JoinOpts { /// Set the custom address that this bot will send in the handshake packet. #[must_use] - pub fn custom_address(mut self, custom_address: ServerAddress) -> Self { - self.custom_address = Some(custom_address); + pub fn custom_server_addr(mut self, server_addr: ServerAddr) -> Self { + self.custom_server_addr = Some(server_addr); self } /// Set the custom resolved address that this bot will use to connect to the /// server. #[must_use] - pub fn custom_resolved_address(mut self, custom_resolved_address: SocketAddr) -> Self { - self.custom_resolved_address = Some(custom_resolved_address); + pub fn custom_socket_addr(mut self, socket_addr: SocketAddr) -> Self { + self.custom_socket_addr = Some(socket_addr); self } + + #[doc(hidden)] + #[deprecated = "renamed to `custom_server_addr`."] + pub fn custom_address(self, server_addr: ServerAddr) -> Self { + self.custom_server_addr(server_addr) + } + #[doc(hidden)] + #[deprecated = "renamed to `custom_socket_addr`."] + pub fn custom_resolved_address(self, socket_addr: SocketAddr) -> Self { + self.custom_socket_addr(socket_addr) + } } diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index 969b1e86..335244cc 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -3,6 +3,7 @@ pub use azalea_client::{Account, Client, Event}; pub use azalea_core::tick::GameTick; +pub use bevy_app::AppExit; // this is necessary to make the macros that reference bevy_ecs work pub use crate::ecs as bevy_ecs; diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index feb3ff38..24e9c1b2 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -10,7 +10,6 @@ use std::{ collections::{HashMap, hash_map}, future::Future, mem, - net::SocketAddr, sync::{ Arc, atomic::{self, AtomicBool}, @@ -19,14 +18,14 @@ use std::{ }; use azalea_client::{ - Account, Client, DefaultPlugins, Event, JoinError, StartClientOpts, + Account, Client, DefaultPlugins, Event, StartClientOpts, auto_reconnect::{AutoReconnectDelay, DEFAULT_RECONNECT_DELAY}, chat::ChatPacket, join::ConnectOpts, start_ecs_runner, }; use azalea_entity::LocalEntity; -use azalea_protocol::{ServerAddress, resolve}; +use azalea_protocol::address::{ResolvableAddr, ResolvedAddr}; use azalea_world::InstanceContainer; use bevy_app::{App, AppExit, PluginGroup, PluginGroupBuilder, Plugins, SubApp}; use bevy_ecs::prelude::*; @@ -35,7 +34,7 @@ use parking_lot::{Mutex, RwLock}; use tokio::{sync::mpsc, task}; use tracing::{debug, error, warn}; -use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, JoinOpts, NoState, StartError}; +use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, JoinOpts, NoState}; /// A swarm is a way to conveniently control many bots at once, while also /// being able to control bots at an individual level when desired. @@ -51,8 +50,7 @@ pub struct Swarm { pub ecs_lock: Arc<Mutex<World>>, // the address is public and mutable so plugins can change it - pub resolved_address: Arc<RwLock<SocketAddr>>, - pub address: Arc<RwLock<ServerAddress>>, + pub address: Arc<RwLock<ResolvedAddr>>, pub instance_container: Arc<RwLock<InstanceContainer>>, @@ -405,28 +403,31 @@ where /// Build this `SwarmBuilder` into an actual [`Swarm`] and join the given /// server. /// - /// The `address` argument can be a `&str`, [`ServerAddress`], or anything - /// that implements `TryInto<ServerAddress>`. + /// The `address` argument can be a `&str`, [`ServerAddr`], + /// [`ResolvedAddr`], or anything else that implements [`ResolvableAddr`]. /// - /// [`ServerAddress`]: azalea_protocol::ServerAddress - pub async fn start(self, address: impl TryInto<ServerAddress>) -> Result<AppExit, StartError> { - // convert the TryInto<ServerAddress> into a ServerAddress - let address: ServerAddress = match address.try_into() { - Ok(address) => address, - Err(_) => return Err(StartError::InvalidAddress), - }; + /// [`ServerAddr`]: azalea_protocol::address::ServerAddr + pub async fn start(self, address: impl ResolvableAddr) -> AppExit { + self.start_with_opts(address, JoinOpts::default()).await + } - self.start_with_default_opts(address, JoinOpts::default()) - .await + #[doc(hidden)] + #[deprecated = "renamed to `start_with_opts`."] + pub async fn start_with_default_opts( + self, + address: impl ResolvableAddr, + default_join_opts: JoinOpts, + ) -> AppExit { + self.start_with_opts(address, default_join_opts).await } /// Do the same as [`Self::start`], but allow passing in default join /// options for the bots. - pub async fn start_with_default_opts( + pub async fn start_with_opts( mut self, - address: impl TryInto<ServerAddress>, - default_join_opts: JoinOpts, - ) -> Result<AppExit, StartError> { + address: impl ResolvableAddr, + join_opts: JoinOpts, + ) -> AppExit { assert_eq!( self.accounts.len(), self.states.len(), @@ -435,17 +436,32 @@ where debug!("Starting Azalea {}", env!("CARGO_PKG_VERSION")); - // convert the TryInto<ServerAddress> into a ServerAddress - let address = match address.try_into() { - Ok(address) => address, - Err(_) => return Err(StartError::InvalidAddress), - }; + let address = if let Some(socket_addr) = join_opts.custom_socket_addr.clone() { + let server_addr = if let Some(server_addr) = join_opts + .custom_server_addr + .clone() + .or_else(|| address.clone().server_addr().ok()) + { + server_addr + } else { + error!( + "Failed to parse address: {address:?}. If this was expected, consider passing in a `ServerAddr` instead." + ); + return AppExit::error(); + }; - let address: ServerAddress = default_join_opts.custom_address.clone().unwrap_or(address); - let resolved_address = if let Some(a) = default_join_opts.custom_resolved_address { - a + ResolvedAddr { + server: server_addr, + socket: socket_addr, + } } else { - resolve::resolve_address(&address).await? + let Ok(addr) = address.clone().resolve().await else { + error!( + "Failed to resolve address: {address:?}. If this was expected, consider resolving the address earlier with `ResolvableAddr::resolve`." + ); + return AppExit::error(); + }; + addr }; let instance_container = Arc::new(RwLock::new(InstanceContainer::default())); @@ -460,14 +476,13 @@ where let local_set = task::LocalSet::new(); - let appexit = local_set.run_until(async move { + let app_exit = local_set.run_until(async move { // start_ecs_runner must be run inside of the LocalSet let (ecs_lock, start_running_systems, appexit_rx) = start_ecs_runner(&mut self.app); let swarm = Swarm { ecs_lock: ecs_lock.clone(), - resolved_address: Arc::new(RwLock::new(resolved_address)), address: Arc::new(RwLock::new(address)), instance_container, @@ -507,7 +522,7 @@ where if let Some(join_delay) = join_delay { // if there's a join delay, then join one by one for ((account, bot_join_opts), state) in accounts.iter().zip(states) { - let mut join_opts = default_join_opts.clone(); + let mut join_opts = join_opts.clone(); join_opts.update(bot_join_opts); let _ = swarm_clone.add_with_opts(account, state, &join_opts).await; tokio::time::sleep(join_delay).await; @@ -517,7 +532,7 @@ where let swarm_borrow = &swarm_clone; join_all(accounts.iter().zip(states).map( |((account, bot_join_opts), state)| async { - let mut join_opts = default_join_opts.clone(); + let mut join_opts = join_opts.clone(); join_opts.update(bot_join_opts); let _ = swarm_borrow .clone() @@ -603,17 +618,17 @@ where } }); - let appexit = appexit_rx + let app_exit = appexit_rx .await .expect("appexit_tx shouldn't be dropped by the ECS runner before sending"); swarm_handler_task.abort(); client_handler_task.abort(); - appexit + app_exit }).await; - Ok(appexit) + app_exit } } @@ -667,7 +682,7 @@ pub type BoxSwarmHandleFn<SS, R> = /// struct SwarmState {} /// /// #[tokio::main] -/// async fn main() { +/// async fn main() -> AppExit { /// let mut accounts = Vec::new(); /// let mut states = Vec::new(); /// @@ -683,7 +698,6 @@ pub type BoxSwarmHandleFn<SS, R> = /// .join_delay(Duration::from_millis(1000)) /// .start("localhost") /// .await -/// .unwrap(); /// } /// /// async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> { @@ -714,11 +728,7 @@ impl Swarm { /// # Errors /// /// Returns an error if the server's address could not be resolved. - pub async fn add<S: Component + Clone>( - &self, - account: &Account, - state: S, - ) -> Result<Client, JoinError> { + pub async fn add<S: Component + Clone>(&self, account: &Account, state: S) -> Client { self.add_with_opts(account, state, &JoinOpts::default()) .await } @@ -735,19 +745,19 @@ impl Swarm { account: &Account, state: S, join_opts: &JoinOpts, - ) -> Result<Client, JoinError> { + ) -> Client { debug!( "add_with_opts called for account {} with opts {join_opts:?}", account.username ); - let address = join_opts - .custom_address - .clone() - .unwrap_or_else(|| self.address.read().clone()); - let resolved_address = join_opts - .custom_resolved_address - .unwrap_or_else(|| *self.resolved_address.read()); + let mut address = self.address.read().clone(); + if let Some(custom_server_addr) = join_opts.custom_server_addr.clone() { + address.server = custom_server_addr; + } + if let Some(custom_socket_addr) = join_opts.custom_socket_addr.clone() { + address.socket = custom_socket_addr; + } let server_proxy = join_opts.server_proxy.clone(); let sessionserver_proxy = join_opts.sessionserver_proxy.clone(); @@ -758,7 +768,6 @@ impl Swarm { account: account.clone(), connect_opts: ConnectOpts { address, - resolved_address, server_proxy, sessionserver_proxy, }, @@ -780,7 +789,7 @@ impl Swarm { rx, swarm_tx, bots_tx, cloned_bot, join_opts, )); - Ok(client) + client } /// Copy the events from a client's receiver into bots_tx, until the bot is |
