diff options
| author | mat <git@matdoes.dev> | 2025-12-28 14:31:41 +0500 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2025-12-28 14:31:41 +0500 |
| commit | 7ab3b8924f64f7eadb6b8928b6fae73cb06e4c2f (patch) | |
| tree | 5add5d27fd7c2638ebe6fab9bd7adcdae28a358d /azalea/src | |
| parent | 9513f42e87f64c409cdb2a100500a50e5a713bac (diff) | |
| download | azalea-drasl-7ab3b8924f64f7eadb6b8928b6fae73cb06e4c2f.tar.xz | |
move Event and auto_reconnect to the azalea crate
Diffstat (limited to 'azalea/src')
| -rw-r--r-- | azalea/src/auto_reconnect.rs | 134 | ||||
| -rw-r--r-- | azalea/src/bot.rs | 6 | ||||
| -rw-r--r-- | azalea/src/builder.rs | 2 | ||||
| -rw-r--r-- | azalea/src/client_impl/mod.rs | 12 | ||||
| -rw-r--r-- | azalea/src/client_impl/movement.rs | 2 | ||||
| -rw-r--r-- | azalea/src/events.rs | 316 | ||||
| -rw-r--r-- | azalea/src/lib.rs | 9 | ||||
| -rw-r--r-- | azalea/src/prelude.rs | 4 | ||||
| -rw-r--r-- | azalea/src/swarm/builder.rs | 7 | ||||
| -rw-r--r-- | azalea/src/swarm/mod.rs | 10 |
10 files changed, 479 insertions, 23 deletions
diff --git a/azalea/src/auto_reconnect.rs b/azalea/src/auto_reconnect.rs new file mode 100644 index 00000000..dd353b7a --- /dev/null +++ b/azalea/src/auto_reconnect.rs @@ -0,0 +1,134 @@ +//! Auto-reconnect to the server when the client is kicked. +//! +//! See [`AutoReconnectPlugin`] for more information. + +use std::time::{Duration, Instant}; + +use bevy_app::prelude::*; +use bevy_ecs::prelude::*; + +use super::{ + disconnect::DisconnectEvent, + join::{ConnectOpts, ConnectionFailedEvent, StartJoinServerEvent}, +}; +use crate::Account; + +/// The default delay that Azalea will use for reconnecting our clients. +/// +/// See [`AutoReconnectPlugin`] for more information. +pub const DEFAULT_RECONNECT_DELAY: Duration = Duration::from_secs(5); + +/// A default plugin that makes clients automatically rejoin the server when +/// they're disconnected. +/// +/// The reconnect delay is configurable globally or per-client with the +/// [`AutoReconnectDelay`] resource/component. Auto reconnecting can be disabled +/// by removing the resource from the ECS. +/// +/// The delay defaults to [`DEFAULT_RECONNECT_DELAY`]. +pub struct AutoReconnectPlugin; +impl Plugin for AutoReconnectPlugin { + fn build(&self, app: &mut App) { + app.insert_resource(AutoReconnectDelay::new(DEFAULT_RECONNECT_DELAY)) + .add_systems( + Update, + (start_rejoin_on_disconnect, rejoin_after_delay) + .chain() + .before(super::join::handle_start_join_server_event), + ); + } +} + +pub fn start_rejoin_on_disconnect( + mut commands: Commands, + mut disconnect_events: MessageReader<DisconnectEvent>, + mut connection_failed_events: MessageReader<ConnectionFailedEvent>, + auto_reconnect_delay_res: Option<Res<AutoReconnectDelay>>, + auto_reconnect_delay_query: Query<&AutoReconnectDelay>, +) { + for entity in disconnect_events + .read() + .map(|e| e.entity) + .chain(connection_failed_events.read().map(|e| e.entity)) + { + let Some(delay) = get_delay( + &auto_reconnect_delay_res, + auto_reconnect_delay_query, + entity, + ) else { + // no auto reconnect + continue; + }; + + let reconnect_after = Instant::now() + delay; + commands.entity(entity).insert(InternalReconnectAfter { + instant: reconnect_after, + }); + } +} + +fn get_delay( + auto_reconnect_delay_res: &Option<Res<AutoReconnectDelay>>, + auto_reconnect_delay_query: Query<&AutoReconnectDelay>, + entity: Entity, +) -> Option<Duration> { + let delay = if let Ok(c) = auto_reconnect_delay_query.get(entity) { + Some(c.delay) + } else { + auto_reconnect_delay_res.as_ref().map(|r| r.delay) + }; + + if delay == Some(Duration::MAX) { + // if the duration is set to max, treat that as autoreconnect being disabled + return None; + } + delay +} + +pub fn rejoin_after_delay( + mut commands: Commands, + mut join_events: MessageWriter<StartJoinServerEvent>, + query: Query<(Entity, &InternalReconnectAfter, &Account, &ConnectOpts)>, +) { + for (entity, reconnect_after, account, connect_opts) in query.iter() { + if Instant::now() >= reconnect_after.instant { + // don't keep trying to reconnect + commands.entity(entity).remove::<InternalReconnectAfter>(); + + // our Entity will be reused since the account has the same uuid + join_events.write(StartJoinServerEvent { + account: account.clone(), + connect_opts: connect_opts.clone(), + start_join_callback_tx: None, + }); + } + } +} + +/// A resource *and* component that indicates how long to wait before +/// reconnecting when we're kicked. +/// +/// Initially, it's a resource in the ECS set to 5 seconds. You can modify +/// the resource to update the global reconnect delay, or insert it as a +/// component to set the individual delay for a single client. +/// +/// You can also remove this resource from the ECS to disable the default +/// auto-reconnecting behavior. Inserting the resource/component again will not +/// make clients that were already disconnected automatically reconnect. +#[derive(Clone, Component, Debug, Resource)] +pub struct AutoReconnectDelay { + pub delay: Duration, +} +impl AutoReconnectDelay { + pub fn new(delay: Duration) -> Self { + Self { delay } + } +} + +/// This is inserted when we're disconnected and indicates when we'll reconnect. +/// +/// This is set based on [`AutoReconnectDelay`]. +#[derive(Clone, Component, Debug)] +pub struct InternalReconnectAfter { + pub instant: Instant, +} diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index 6f39c2fe..73b225b2 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -130,9 +130,7 @@ impl Client { /// Mine a block. /// /// This won't turn the bot's head towards the block, so if that's necessary - /// you'll have to do that yourself with [`look_at`]. - /// - /// [`look_at`]: crate::prelude::BotClientExt::look_at + /// you'll have to do that yourself with [`look_at`](Client::look_at). pub async fn mine(&self, position: BlockPos) { self.start_mining(position); @@ -211,5 +209,7 @@ impl PluginGroup for DefaultBotPlugins { .add(crate::auto_respawn::AutoRespawnPlugin) .add(crate::accept_resource_packs::AcceptResourcePacksPlugin) .add(crate::tick_broadcast::TickBroadcastPlugin) + .add(crate::events::EventsPlugin) + .add(crate::auto_reconnect::AutoReconnectPlugin) } } diff --git a/azalea/src/builder.rs b/azalea/src/builder.rs index 91fb9d5e..8151b3a1 100644 --- a/azalea/src/builder.rs +++ b/azalea/src/builder.rs @@ -150,7 +150,7 @@ where /// If this function isn't called, then our client will reconnect after /// [`DEFAULT_RECONNECT_DELAY`]. /// - /// [`DEFAULT_RECONNECT_DELAY`]: azalea_client::auto_reconnect::DEFAULT_RECONNECT_DELAY + /// [`DEFAULT_RECONNECT_DELAY`]: crate::auto_reconnect::DEFAULT_RECONNECT_DELAY #[must_use] pub fn reconnect_after(mut self, delay: impl Into<Option<Duration>>) -> Self { self.swarm.reconnect_after = delay.into(); diff --git a/azalea/src/client_impl/mod.rs b/azalea/src/client_impl/mod.rs index 2f5fbf7d..36995656 100644 --- a/azalea/src/client_impl/mod.rs +++ b/azalea/src/client_impl/mod.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc}; use azalea_auth::game_profile::GameProfile; use azalea_client::{ - Account, DefaultPlugins, Event, + Account, DefaultPlugins, connection::RawConnection, disconnect::DisconnectEvent, join::{ConnectOpts, StartJoinServerEvent}, @@ -40,6 +40,8 @@ use parking_lot::{Mutex, RwLock}; use tokio::sync::mpsc; use uuid::Uuid; +use crate::events::{Event, LocalPlayerEvents}; + pub mod attack; pub mod chat; pub mod client_information; @@ -210,7 +212,6 @@ impl Client { ecs_lock.lock().write_message(StartJoinServerEvent { account, connect_opts, - event_sender, start_join_callback_tx: Some(start_join_callback_tx), }); @@ -218,6 +219,13 @@ impl Client { "start_join_callback should not be dropped before sending a message, this is a bug in Azalea", ); + if let Some(event_sender) = event_sender { + ecs_lock + .lock() + .entity_mut(entity) + .insert(LocalPlayerEvents(event_sender)); + } + Client::new(entity, ecs_lock) } diff --git a/azalea/src/client_impl/movement.rs b/azalea/src/client_impl/movement.rs index 08624263..b47da9a7 100644 --- a/azalea/src/client_impl/movement.rs +++ b/azalea/src/client_impl/movement.rs @@ -28,7 +28,7 @@ impl Client { /// Whether the client is currently trying to sneak. /// - /// You may want to check the [`Pose`] instead. + /// You may want to check the [`Pose`](azalea_entity::Pose) instead. pub fn crouching(&self) -> bool { self.query_self::<&PhysicsState, _>(|p| p.trying_to_crouch) } diff --git a/azalea/src/events.rs b/azalea/src/events.rs new file mode 100644 index 00000000..0920dbd8 --- /dev/null +++ b/azalea/src/events.rs @@ -0,0 +1,316 @@ +//! Defines the [`enum@Event`] enum and makes those events trigger when they're +//! sent in the ECS. + +use std::sync::Arc; + +use azalea_chat::FormattedText; +use azalea_core::{position::ChunkPos, tick::GameTick}; +use azalea_entity::{Dead, InLoadedChunk}; +use azalea_protocol::packets::game::c_player_combat_kill::ClientboundPlayerCombatKill; +use azalea_world::{InstanceName, MinecraftEntityId}; +use bevy_app::{App, Plugin, PreUpdate, Update}; +use bevy_ecs::prelude::*; +use derive_more::{Deref, DerefMut}; +use tokio::sync::mpsc; + +use crate::{ + chat::{ChatPacket, ChatReceivedEvent}, + chunks::ReceiveChunkEvent, + disconnect::DisconnectEvent, + packet::game::{ + AddPlayerEvent, DeathEvent, KeepAliveEvent, RemovePlayerEvent, UpdatePlayerEvent, + }, + player::PlayerInfo, +}; + +// (for contributors): +// HOW TO ADD A NEW (packet based) EVENT: +// - Add it as an ECS event first: +// - Make a struct that contains an entity field and some data fields (look +// in packet/game/events.rs for examples. These structs should always have +// their names end with "Event". +// - (the `entity` field is the local player entity that's receiving the +// event) +// - In the GamePacketHandler, you always have a `player` field that you can +// use. +// - Add the event struct in PacketPlugin::build +// - (in the `impl Plugin for PacketPlugin`) +// - To get the event writer, you have to get an MessageWriter<ThingEvent>. +// Look at other packets in packet/game/mod.rs for examples. +// +// At this point, you've created a new ECS event. That's annoying for bots to +// use though, so you might wanna add it to the Event enum too: +// - In this file, add a new variant to that Event enum with the same name +// as your event (without the "Event" suffix). +// - Create a new system function like the other ones here, and put that +// system function in the `impl Plugin for EventsPlugin` + +/// Something that happened in-game, such as a tick passing or chat message +/// being sent. +/// +/// Note: Events are sent before they're processed, so for example game ticks +/// happen at the beginning of a tick before anything has happened. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum Event { + /// Happens right after the bot switches into the Game state, but before + /// it's actually spawned. + /// + /// This can be useful for setting the client information with + /// [`Client::set_client_information`], so the packet doesn't have to be + /// sent twice. + /// + /// You may want to use [`Event::Spawn`] instead to wait for the bot to be + /// in the world. + /// + /// [`Client::set_client_information`]: crate::Client::set_client_information + Init, + /// Fired when we receive a login packet, which is after [`Event::Init`] but + /// before [`Event::Spawn`]. You usually want [`Event::Spawn`] instead. + /// + /// Your position may be [`Vec3::ZERO`] immediately after you receive this + /// event, but it'll be ready by the time you get [`Event::Spawn`]. + /// + /// It's possible for this event to be sent multiple times per client if a + /// server sends multiple login packets (like when switching worlds). + /// + /// [`Vec3::ZERO`]: azalea_core::position::Vec3::ZERO + Login, + /// Fired when the player fully spawns into the world (is in a loaded chunk) + /// and is ready to interact with it. + /// + /// This is usually the event you should listen for when waiting for the bot + /// to be ready. + /// + /// This event will be sent every time the client respawns or switches + /// worlds, as long as the server sends chunks to the client. + Spawn, + /// A chat message was sent in the game chat. + Chat(ChatPacket), + /// Happens 20 times per second, but only when the world is loaded. + Tick, + #[cfg(feature = "packet-event")] + /// We received a packet from the server. + /// + /// ``` + /// # use azalea::Event; + /// # use azalea_protocol::packets::game::ClientboundGamePacket; + /// # async fn example(event: Event) { + /// # match event { + /// Event::Packet(packet) => match &*packet { + /// ClientboundGamePacket::Login(_) => { + /// println!("login packet"); + /// } + /// _ => {} + /// }, + /// # _ => {} + /// # } + /// # } + /// ``` + Packet(Arc<azalea_protocol::packets::game::ClientboundGamePacket>), + /// A player joined the game (or more specifically, was added to the tab + /// list). + AddPlayer(PlayerInfo), + /// A player left the game (or maybe is still in the game and was just + /// removed from the tab list). + RemovePlayer(PlayerInfo), + /// A player was updated in the tab list (gamemode, display + /// name, or latency changed). + UpdatePlayer(PlayerInfo), + /// The client player died in-game. + Death(Option<Arc<ClientboundPlayerCombatKill>>), + /// A `KeepAlive` packet was sent by the server. + KeepAlive(u64), + /// The client disconnected from the server. + Disconnect(Option<FormattedText>), + ReceiveChunk(ChunkPos), +} + +/// A component that contains an event sender for events that are only +/// received by local players. +/// +/// The receiver for this is returned by +/// [`Client::start_client`](crate::Client::start_client). +#[derive(Component, Deref, DerefMut)] +pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>); + +pub struct EventsPlugin; +impl Plugin for EventsPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + Update, + ( + chat_listener, + login_listener, + spawn_listener, + #[cfg(feature = "packet-event")] + packet_listener, + add_player_listener, + update_player_listener, + remove_player_listener, + keepalive_listener, + death_listener.after(azalea_client::packet::death_event_on_0_health), + disconnect_listener, + receive_chunk_listener, + ), + ) + .add_systems( + PreUpdate, + init_listener.before(super::connection::read_packets), + ) + .add_systems(GameTick, tick_listener); + } +} + +// when LocalPlayerEvents is added, it means the client just started +pub fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) { + for local_player_events in &query { + let _ = local_player_events.send(Event::Init); + } +} + +// when MinecraftEntityId is added, it means the player is now in the world +pub fn login_listener( + query: Query<(Entity, &LocalPlayerEvents), Added<MinecraftEntityId>>, + mut commands: Commands, +) { + for (entity, local_player_events) in &query { + let _ = local_player_events.send(Event::Login); + commands.entity(entity).remove::<SentSpawnEvent>(); + } +} + +/// A unit struct component that indicates that the entity has sent +/// [`Event::Spawn`]. +/// +/// This is just used internally by the [`spawn_listener`] system to avoid +/// sending the event twice if we stop being in an unloaded chunk. It's removed +/// when we receive a login packet. +#[derive(Component)] +pub struct SentSpawnEvent; +#[allow(clippy::type_complexity)] +pub fn spawn_listener( + query: Query<(Entity, &LocalPlayerEvents), (Added<InLoadedChunk>, Without<SentSpawnEvent>)>, + mut commands: Commands, +) { + for (entity, local_player_events) in &query { + let _ = local_player_events.send(Event::Spawn); + commands.entity(entity).insert(SentSpawnEvent); + } +} + +pub fn chat_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<ChatReceivedEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::Chat(event.packet.clone())); + } + } +} + +// only tick if we're in a world +pub fn tick_listener(query: Query<&LocalPlayerEvents, With<InstanceName>>) { + for local_player_events in &query { + let _ = local_player_events.send(Event::Tick); + } +} + +#[cfg(feature = "packet-event")] +pub fn packet_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<super::packet::game::ReceiveGamePacketEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::Packet(event.packet.clone())); + } + } +} + +pub fn add_player_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<AddPlayerEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::AddPlayer(event.info.clone())); + } + } +} + +pub fn update_player_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<UpdatePlayerEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::UpdatePlayer(event.info.clone())); + } + } +} + +pub fn remove_player_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<RemovePlayerEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::RemovePlayer(event.info.clone())); + } + } +} + +pub fn death_listener(query: Query<&LocalPlayerEvents>, mut events: MessageReader<DeathEvent>) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::Death(event.packet.clone().map(|p| p.into()))); + } + } +} + +/// Send the "Death" event for [`LocalEntity`]s that died with no reason. +/// +/// [`LocalEntity`]: azalea_entity::LocalEntity +pub fn dead_component_listener(query: Query<&LocalPlayerEvents, Added<Dead>>) { + for local_player_events in &query { + local_player_events.send(Event::Death(None)).unwrap(); + } +} + +pub fn keepalive_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<KeepAliveEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::KeepAlive(event.id)); + } + } +} + +pub fn disconnect_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<DisconnectEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::Disconnect(event.reason.clone())); + } + } +} + +pub fn receive_chunk_listener( + query: Query<&LocalPlayerEvents>, + mut events: MessageReader<ReceiveChunkEvent>, +) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::ReceiveChunk(ChunkPos::new( + event.packet.x, + event.packet.z, + ))); + } + } +} diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 2105daee..351c956a 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -2,12 +2,14 @@ #![feature(type_changing_struct_update)] pub mod accept_resource_packs; +pub mod auto_reconnect; pub mod auto_respawn; pub mod auto_tool; pub mod bot; mod builder; mod client_impl; pub mod container; +pub mod events; mod join_opts; pub mod nearest_entity; pub mod pathfinder; @@ -51,11 +53,10 @@ pub use builder::ClientBuilder; use futures::future::BoxFuture; pub use join_opts::JoinOpts; -pub use crate::client_impl::Client; +pub use crate::{client_impl::Client, events::Event}; -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; +pub type BoxHandleFn<S, R> = Box<dyn Fn(Client, Event, S) -> BoxFuture<'static, R> + Send>; +pub type HandleFn<S, Fut> = fn(Client, Event, S) -> Fut; /// A marker that can be used in place of a State in [`ClientBuilder`] or /// [`SwarmBuilder`]. diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index 2675c8f5..d39e50c4 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -1,14 +1,14 @@ //! The Azalea prelude. Things that are necessary for a bare-bones bot are //! re-exported here. -pub use azalea_client::{Account, Event}; +pub use azalea_client::Account; 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; pub use crate::{ - Client, ClientBuilder, + Client, ClientBuilder, Event, ecs::{component::Component, resource::Resource}, pathfinder::PathfinderClientExt, }; diff --git a/azalea/src/swarm/builder.rs b/azalea/src/swarm/builder.rs index 4eae7eab..860e47be 100644 --- a/azalea/src/swarm/builder.rs +++ b/azalea/src/swarm/builder.rs @@ -8,11 +8,7 @@ use std::{ time::Duration, }; -use azalea_client::{ - Account, DefaultPlugins, - auto_reconnect::{AutoReconnectDelay, DEFAULT_RECONNECT_DELAY}, - start_ecs_runner, -}; +use azalea_client::{Account, DefaultPlugins, start_ecs_runner}; use azalea_protocol::address::{ResolvableAddr, ResolvedAddr}; use azalea_world::InstanceContainer; use bevy_app::{App, AppExit, Plugins, SubApp}; @@ -24,6 +20,7 @@ use tracing::{debug, error, warn}; use crate::{ BoxHandleFn, HandleFn, JoinOpts, NoState, + auto_reconnect::{AutoReconnectDelay, DEFAULT_RECONNECT_DELAY}, bot::DefaultBotPlugins, swarm::{ BoxSwarmHandleFn, DefaultSwarmPlugins, NoSwarmState, Swarm, SwarmEvent, SwarmHandleFn, diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index c0f9cbca..48ac20a3 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -12,7 +12,7 @@ use std::sync::{ atomic::{self, AtomicBool}, }; -use azalea_client::{Account, Event, chat::ChatPacket, join::ConnectOpts}; +use azalea_client::{Account, chat::ChatPacket, join::ConnectOpts}; use azalea_entity::LocalEntity; use azalea_protocol::address::ResolvedAddr; use azalea_world::InstanceContainer; @@ -45,7 +45,7 @@ pub struct Swarm { pub instance_container: Arc<RwLock<InstanceContainer>>, /// This is used internally to make the client handler function work. - pub(crate) bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>, + pub(crate) bots_tx: mpsc::UnboundedSender<(Option<crate::Event>, Client)>, /// This is used internally to make the swarm handler function work. pub(crate) swarm_tx: mpsc::UnboundedSender<SwarmEvent>, } @@ -207,9 +207,9 @@ impl Swarm { /// Copy the events from a client's receiver into bots_tx, until the bot is /// removed from the ECS. async fn event_copying_task( - mut rx: mpsc::UnboundedReceiver<Event>, + mut rx: mpsc::UnboundedReceiver<crate::Event>, swarm_tx: mpsc::UnboundedSender<SwarmEvent>, - bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>, + bots_tx: mpsc::UnboundedSender<(Option<crate::Event>, Client)>, bot: Client, join_opts: JoinOpts, ) { @@ -246,7 +246,7 @@ impl Swarm { } } - if let Event::Disconnect(_) = event { + if let crate::Event::Disconnect(_) = event { debug!( "Sending SwarmEvent::Disconnect due to receiving an Event::Disconnect from client {}", bot.entity |
