veloren_voxygen/menu/main/
client_init.rs1use client::{
2 Client, ClientInitStage, ServerInfo,
3 addr::ConnectionArgs,
4 error::{Error as ClientError, NetworkConnectError, NetworkError},
5};
6use common_net::msg::ClientType;
7use crossbeam_channel::{Receiver, Sender, TryRecvError, unbounded};
8use std::{
9 path::Path,
10 sync::{
11 Arc,
12 atomic::{AtomicBool, Ordering},
13 },
14 time::Duration,
15};
16use tokio::runtime;
17use tracing::{trace, warn};
18
19#[derive(Debug)]
20#[expect(clippy::enum_variant_names)] pub enum Error {
22 ClientError {
23 error: ClientError,
24 mismatched_server_info: Option<ServerInfo>,
25 },
26 ClientCrashed,
27 ServerNotFound,
28}
29
30#[expect(clippy::large_enum_variant)]
31pub enum Msg {
32 IsAuthTrusted(String),
33 Done(Result<Client, Error>),
34}
35
36pub struct AuthTrust(String, bool);
37
38pub struct ClientInit {
42 rx: Receiver<Msg>,
43 stage_rx: Receiver<ClientInitStage>,
44 trust_tx: Sender<AuthTrust>,
45 cancel: Arc<AtomicBool>,
46}
47impl ClientInit {
48 pub fn new(
49 connection_args: ConnectionArgs,
50 username: String,
51 password: String,
52 runtime: Arc<runtime::Runtime>,
53 locale: Option<String>,
54 config_dir: &Path,
55 client_type: ClientType,
56 ) -> Self {
57 let (tx, rx) = unbounded();
58 let (trust_tx, trust_rx) = unbounded();
59 let (init_stage_tx, init_stage_rx) = unbounded();
60 let cancel = Arc::new(AtomicBool::new(false));
61 let cancel2 = Arc::clone(&cancel);
62
63 let runtime2 = Arc::clone(&runtime);
64 let config_dir = config_dir.to_path_buf();
65
66 runtime.spawn(async move {
67 let trust_fn = |auth_server: &str| {
68 let _ = tx.send(Msg::IsAuthTrusted(auth_server.to_string()));
69 trust_rx
70 .recv()
71 .map(|AuthTrust(server, trust)| trust && server == *auth_server)
72 .unwrap_or(false)
73 };
74
75 let mut last_err = None;
76
77 const FOUR_MINUTES_RETRIES: u64 = 48;
78 'tries: for _ in 0..FOUR_MINUTES_RETRIES {
79 if cancel2.load(Ordering::Relaxed) {
80 break;
81 }
82 let mut mismatched_server_info = None;
83 match Client::new(
84 connection_args.clone(),
85 Arc::clone(&runtime2),
86 &mut mismatched_server_info,
87 &username,
88 &password,
89 locale.clone(),
90 trust_fn,
91 &|stage| {
92 let _ = init_stage_tx.send(stage);
93 },
94 crate::ecs::sys::add_local_systems,
95 config_dir.clone(),
96 client_type,
97 )
98 .await
99 {
100 Ok(client) => {
101 let _ = tx.send(Msg::Done(Ok(client)));
102 tokio::task::block_in_place(move || drop(runtime2));
103 return;
104 },
105 Err(ClientError::NetworkErr(NetworkError::ConnectFailed(
106 NetworkConnectError::Io(e),
107 ))) => {
108 warn!(?e, "Failed to connect to the server. Retrying...");
109 },
110 Err(e) => {
111 trace!(?e, "Aborting server connection attempt");
112 last_err = Some(Error::ClientError {
113 error: e,
114 mismatched_server_info,
115 });
116 break 'tries;
117 },
118 }
119 tokio::time::sleep(Duration::from_secs(5)).await;
120 }
121
122 let _ = tx.send(Msg::Done(Err(last_err.unwrap_or(Error::ServerNotFound))));
126
127 tokio::task::block_in_place(move || drop(runtime2));
129 });
130
131 ClientInit {
132 rx,
133 stage_rx: init_stage_rx,
134 trust_tx,
135 cancel,
136 }
137 }
138
139 pub fn poll(&self) -> Option<Msg> {
143 match self.rx.try_recv() {
144 Ok(msg) => Some(msg),
145 Err(TryRecvError::Empty) => None,
146 Err(TryRecvError::Disconnected) => Some(Msg::Done(Err(Error::ClientCrashed))),
147 }
148 }
149
150 pub fn stage_update(&self) -> Option<ClientInitStage> { self.stage_rx.try_recv().ok() }
152
153 pub fn auth_trust(&self, auth_server: String, trusted: bool) {
155 let _ = self.trust_tx.send(AuthTrust(auth_server, trusted));
156 }
157
158 pub fn cancel(&mut self) { self.cancel.store(true, Ordering::Relaxed); }
159}
160
161impl Drop for ClientInit {
162 fn drop(&mut self) { self.cancel(); }
163}