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/auto_reconnect.rs | |
| parent | 9513f42e87f64c409cdb2a100500a50e5a713bac (diff) | |
| download | azalea-drasl-7ab3b8924f64f7eadb6b8928b6fae73cb06e4c2f.tar.xz | |
move Event and auto_reconnect to the azalea crate
Diffstat (limited to 'azalea/src/auto_reconnect.rs')
| -rw-r--r-- | azalea/src/auto_reconnect.rs | 134 |
1 files changed, 134 insertions, 0 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, +} |
