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