1use std::time::{Duration, Instant};
6
7use bevy_app::prelude::*;
8use bevy_ecs::prelude::*;
9
10use super::{
11 disconnect::DisconnectEvent,
12 join::{ConnectOpts, ConnectionFailedEvent, StartJoinServerEvent},
13};
14use crate::Account;
15
16pub const DEFAULT_RECONNECT_DELAY: Duration = Duration::from_secs(5);
20
21pub struct AutoReconnectPlugin;
30impl Plugin for AutoReconnectPlugin {
31 fn build(&self, app: &mut App) {
32 app.insert_resource(AutoReconnectDelay::new(DEFAULT_RECONNECT_DELAY))
33 .add_systems(
34 Update,
35 (start_rejoin_on_disconnect, rejoin_after_delay)
36 .chain()
37 .before(super::join::handle_start_join_server_event),
38 );
39 }
40}
41
42pub fn start_rejoin_on_disconnect(
43 mut commands: Commands,
44 mut disconnect_events: MessageReader<DisconnectEvent>,
45 mut connection_failed_events: MessageReader<ConnectionFailedEvent>,
46 auto_reconnect_delay_res: Option<Res<AutoReconnectDelay>>,
47 auto_reconnect_delay_query: Query<&AutoReconnectDelay>,
48) {
49 for entity in disconnect_events
50 .read()
51 .map(|e| e.entity)
52 .chain(connection_failed_events.read().map(|e| e.entity))
53 {
54 let Some(delay) = get_delay(
55 &auto_reconnect_delay_res,
56 auto_reconnect_delay_query,
57 entity,
58 ) else {
59 continue;
61 };
62
63 let reconnect_after = Instant::now() + delay;
64 commands.entity(entity).insert(InternalReconnectAfter {
65 instant: reconnect_after,
66 });
67 }
68}
69
70fn get_delay(
71 auto_reconnect_delay_res: &Option<Res<AutoReconnectDelay>>,
72 auto_reconnect_delay_query: Query<&AutoReconnectDelay>,
73 entity: Entity,
74) -> Option<Duration> {
75 let delay = if let Ok(c) = auto_reconnect_delay_query.get(entity) {
76 Some(c.delay)
77 } else {
78 auto_reconnect_delay_res.as_ref().map(|r| r.delay)
79 };
80
81 if delay == Some(Duration::MAX) {
82 return None;
84 }
85 delay
86}
87
88pub fn rejoin_after_delay(
89 mut commands: Commands,
90 mut join_events: MessageWriter<StartJoinServerEvent>,
91 query: Query<(Entity, &InternalReconnectAfter, &Account, &ConnectOpts)>,
92) {
93 for (entity, reconnect_after, account, connect_opts) in query.iter() {
94 if Instant::now() >= reconnect_after.instant {
95 commands.entity(entity).remove::<InternalReconnectAfter>();
97
98 join_events.write(StartJoinServerEvent {
100 account: account.clone(),
101 connect_opts: connect_opts.clone(),
102 start_join_callback_tx: None,
103 });
104 }
105 }
106}
107
108#[derive(Clone, Component, Debug, Resource)]
119pub struct AutoReconnectDelay {
120 pub delay: Duration,
121}
122impl AutoReconnectDelay {
123 pub fn new(delay: Duration) -> Self {
124 Self { delay }
125 }
126}
127
128#[derive(Clone, Component, Debug)]
132pub struct InternalReconnectAfter {
133 pub instant: Instant,
134}