mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add Quin support, as this is not yet very much tested it needs to be activated in the settings of SERVER and CLIENT.
Server: provide a certificate file and key file via the settings. When provided it will then listen on TCP and QUIC, if not provided it will be TCP only. The certificate must be known by the client, so you might get problems with self-signed certificates. ```ron quic_files: Some(( cert: "/home/user/veloren_cert.pem", key: "/home/user/veloren_key.key", )), ``` Client: activate the voxygen settin `use_quic: true` to try to connect to the quic backend of a server.
This commit is contained in:
parent
0cf0f59fa7
commit
b443e4dd31
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5477,6 +5477,7 @@ dependencies = [
|
||||
"hashbrown",
|
||||
"image",
|
||||
"num 0.4.0",
|
||||
"quinn",
|
||||
"rayon",
|
||||
"ron",
|
||||
"rustyline",
|
||||
@ -5745,6 +5746,7 @@ dependencies = [
|
||||
"portpicker",
|
||||
"prometheus",
|
||||
"prometheus-hyper",
|
||||
"quinn",
|
||||
"rand 0.8.3",
|
||||
"rand_distr",
|
||||
"rayon",
|
||||
|
@ -18,11 +18,12 @@ common-base = { package = "veloren-common-base", path = "../common/base" }
|
||||
common-state = { package = "veloren-common-state", path = "../common/state", default-features = false }
|
||||
common-systems = { package = "veloren-common-systems", path = "../common/systems", default-features = false }
|
||||
common-net = { package = "veloren-common-net", path = "../common/net" }
|
||||
network = { package = "veloren-network", path = "../network", features = ["compression"], default-features = false }
|
||||
network = { package = "veloren-network", path = "../network", features = ["compression","quic"], default-features = false }
|
||||
|
||||
byteorder = "1.3.2"
|
||||
futures-util = "0.3.7"
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
|
||||
quinn = "0.7.2"
|
||||
image = { version = "0.23.12", default-features = false, features = ["png"] }
|
||||
num = "0.4"
|
||||
tracing = { version = "0.1", default-features = false }
|
||||
|
@ -4,47 +4,80 @@ use tracing::trace;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConnectionArgs {
|
||||
IpAndPort(Vec<SocketAddr>),
|
||||
///hostname: (hostname|ip):[<port>]
|
||||
Quic {
|
||||
hostname: String,
|
||||
prefer_ipv6: bool,
|
||||
},
|
||||
///hostname: (hostname|ip):[<port>]
|
||||
Tcp {
|
||||
hostname: String,
|
||||
prefer_ipv6: bool,
|
||||
},
|
||||
Mpsc(u64),
|
||||
}
|
||||
|
||||
impl ConnectionArgs {
|
||||
const DEFAULT_PORT: u16 = 14004;
|
||||
}
|
||||
|
||||
/// Parse ip address or resolves hostname.
|
||||
/// Note: If you use an ipv6 address, the number after the last
|
||||
/// colon will be used as the port unless you use [] around the address.
|
||||
pub async fn resolve(
|
||||
/* <hostname/ip>:[<port>] */ server_address: &str,
|
||||
prefer_ipv6: bool,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
// `lookup_host` will internally try to parse it as a SocketAddr
|
||||
// 1. Assume it's a hostname + port
|
||||
match lookup_host(server_address).await {
|
||||
Ok(s) => {
|
||||
trace!("Host lookup succeeded");
|
||||
Ok(Self::sort_ipv6(s, prefer_ipv6))
|
||||
},
|
||||
Err(e) => {
|
||||
// 2. Assume its a hostname without port
|
||||
match lookup_host((server_address, Self::DEFAULT_PORT)).await {
|
||||
Ok(s) => {
|
||||
trace!("Host lookup without ports succeeded");
|
||||
Ok(Self::sort_ipv6(s, prefer_ipv6))
|
||||
},
|
||||
Err(_) => Err(e), // Todo: evaluate returning both errors
|
||||
}
|
||||
/// Parse ip address or resolves hostname.
|
||||
/// Note: If you use an ipv6 address, the number after the last
|
||||
/// colon will be used as the port unless you use [] around the address.
|
||||
pub(crate) async fn resolve(
|
||||
address: &str,
|
||||
prefer_ipv6: bool,
|
||||
) -> Result<Vec<SocketAddr>, std::io::Error> {
|
||||
// `lookup_host` will internally try to parse it as a SocketAddr
|
||||
// 1. Assume it's a hostname + port
|
||||
match lookup_host(address).await {
|
||||
Ok(s) => {
|
||||
trace!("Host lookup succeeded");
|
||||
Ok(sort_ipv6(s, prefer_ipv6))
|
||||
},
|
||||
Err(e) => {
|
||||
// 2. Assume its a hostname without port
|
||||
match lookup_host((address, ConnectionArgs::DEFAULT_PORT)).await {
|
||||
Ok(s) => {
|
||||
trace!("Host lookup without ports succeeded");
|
||||
Ok(sort_ipv6(s, prefer_ipv6))
|
||||
},
|
||||
Err(_) => Err(e), // Todo: evaluate returning both errors
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn try_connect<F>(
|
||||
network: &network::Network,
|
||||
address: &str,
|
||||
prefer_ipv6: bool,
|
||||
f: F,
|
||||
) -> Result<network::Participant, crate::error::Error>
|
||||
where
|
||||
F: Fn(std::net::SocketAddr) -> network::ConnectAddr,
|
||||
{
|
||||
use crate::error::Error;
|
||||
let mut participant = None;
|
||||
for addr in resolve(&address, prefer_ipv6)
|
||||
.await
|
||||
.map_err(|e| Error::HostnameLookupFailed(e))?
|
||||
{
|
||||
match network.connect(f(addr)).await {
|
||||
Ok(p) => {
|
||||
participant = Some(Ok(p));
|
||||
break;
|
||||
},
|
||||
Err(e) => participant = Some(Err(Error::NetworkErr(e))),
|
||||
}
|
||||
}
|
||||
participant.unwrap_or_else(|| Err(Error::Other("No Ip Addr provided".to_string())))
|
||||
}
|
||||
|
||||
fn sort_ipv6(s: impl Iterator<Item = SocketAddr>, prefer_ipv6: bool) -> Self {
|
||||
let (mut first_addrs, mut second_addrs) =
|
||||
s.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
|
||||
let addr = std::iter::Iterator::chain(first_addrs.drain(..), second_addrs.drain(..))
|
||||
.collect::<Vec<_>>();
|
||||
ConnectionArgs::IpAndPort(addr)
|
||||
}
|
||||
fn sort_ipv6(s: impl Iterator<Item = SocketAddr>, prefer_ipv6: bool) -> Vec<SocketAddr> {
|
||||
let (mut first_addrs, mut second_addrs) =
|
||||
s.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
|
||||
std::iter::Iterator::chain(first_addrs.drain(..), second_addrs.drain(..)).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -18,6 +18,7 @@ pub enum Error {
|
||||
AuthClientError(AuthClientError),
|
||||
AuthServerUrlInvalid(String),
|
||||
AuthServerNotTrusted,
|
||||
HostnameLookupFailed(std::io::Error),
|
||||
Banned(String),
|
||||
/// Persisted character data is invalid or missing
|
||||
InvalidCharacter,
|
||||
|
@ -203,7 +203,6 @@ pub struct CharacterList {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new `Client`.
|
||||
pub async fn new(
|
||||
addr: ConnectionArgs,
|
||||
view_distance: Option<u32>,
|
||||
@ -214,20 +213,23 @@ impl Client {
|
||||
let network = Network::new(Pid::new(), &runtime);
|
||||
|
||||
let participant = match addr {
|
||||
ConnectionArgs::IpAndPort(addrs) => {
|
||||
// Try to connect to all IP's and return the first that works
|
||||
let mut participant = None;
|
||||
for addr in addrs {
|
||||
match network.connect(ConnectAddr::Tcp(addr)).await {
|
||||
Ok(p) => {
|
||||
participant = Some(Ok(p));
|
||||
break;
|
||||
},
|
||||
Err(e) => participant = Some(Err(Error::NetworkErr(e))),
|
||||
}
|
||||
}
|
||||
participant
|
||||
.unwrap_or_else(|| Err(Error::Other("No Ip Addr provided".to_string())))?
|
||||
ConnectionArgs::Tcp {
|
||||
hostname,
|
||||
prefer_ipv6,
|
||||
} => {
|
||||
addr::try_connect(&network, &hostname, prefer_ipv6, |a| ConnectAddr::Tcp(a)).await?
|
||||
},
|
||||
ConnectionArgs::Quic {
|
||||
hostname,
|
||||
prefer_ipv6,
|
||||
} => {
|
||||
let mut config = quinn::ClientConfigBuilder::default();
|
||||
config.protocols(&[b"VELOREN"]);
|
||||
let config = config.build();
|
||||
addr::try_connect(&network, &hostname, prefer_ipv6, |a| {
|
||||
ConnectAddr::Quic(a, config.clone(), hostname.clone())
|
||||
})
|
||||
.await?
|
||||
},
|
||||
ConnectionArgs::Mpsc(id) => network.connect(ConnectAddr::Mpsc(id)).await?,
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ common-state = { package = "veloren-common-state", path = "../common/state" }
|
||||
common-systems = { package = "veloren-common-systems", path = "../common/systems" }
|
||||
common-net = { package = "veloren-common-net", path = "../common/net" }
|
||||
world = { package = "veloren-world", path = "../world" }
|
||||
network = { package = "veloren-network", path = "../network", features = ["metrics", "compression"], default-features = false }
|
||||
network = { package = "veloren-network", path = "../network", features = ["metrics", "compression", "quic"], default-features = false }
|
||||
|
||||
# inline_tweak = "1.0.8"
|
||||
|
||||
@ -33,6 +33,7 @@ vek = { version = "0.14.1", features = ["serde"] }
|
||||
futures-util = "0.3.7"
|
||||
tokio = { version = "1", default-features = false, features = ["rt"] }
|
||||
prometheus-hyper = "0.1.2"
|
||||
quinn = "0.7.2"
|
||||
atomicwrites = "0.3.0"
|
||||
chrono = { version = "0.4.9", features = ["serde"] }
|
||||
humantime = "2.1.0"
|
||||
|
@ -398,6 +398,38 @@ impl Server {
|
||||
});
|
||||
runtime.block_on(network.listen(ListenAddr::Tcp(settings.gameserver_address)))?;
|
||||
runtime.block_on(network.listen(ListenAddr::Mpsc(14004)))?;
|
||||
if let Some(quic) = &settings.quic_files {
|
||||
use std::fs;
|
||||
match || -> Result<_, Box<dyn std::error::Error>> {
|
||||
let mut server_config =
|
||||
quinn::ServerConfigBuilder::new(quinn::ServerConfig::default());
|
||||
server_config.protocols(&[b"VELOREN"]);
|
||||
let key = fs::read(&quic.key)?;
|
||||
let key = if quic.key.extension().map_or(false, |x| x == "der") {
|
||||
quinn::PrivateKey::from_der(&key)?
|
||||
} else {
|
||||
quinn::PrivateKey::from_pem(&key)?
|
||||
};
|
||||
let cert_chain = fs::read(&quic.cert)?;
|
||||
let cert_chain = if quic.cert.extension().map_or(false, |x| x == "der") {
|
||||
quinn::CertificateChain::from_certs(Some(
|
||||
quinn::Certificate::from_der(&cert_chain).unwrap(),
|
||||
))
|
||||
} else {
|
||||
quinn::CertificateChain::from_pem(&cert_chain)?
|
||||
};
|
||||
server_config.certificate(cert_chain, key)?;
|
||||
Ok(server_config.build())
|
||||
}() {
|
||||
Ok(server_config) => {
|
||||
runtime.block_on(
|
||||
network
|
||||
.listen(ListenAddr::Quic(settings.gameserver_address, server_config)),
|
||||
)?;
|
||||
},
|
||||
Err(e) => error!(?e, "Failed to load Quic Certificate, run without Quic"),
|
||||
}
|
||||
}
|
||||
let connection_handler = ConnectionHandler::new(network, &runtime);
|
||||
|
||||
// Initiate real-time world simulation
|
||||
|
@ -33,12 +33,19 @@ const BANLIST_FILENAME: &str = "banlist.ron";
|
||||
const SERVER_DESCRIPTION_FILENAME: &str = "description.ron";
|
||||
const ADMINS_FILENAME: &str = "admins.ron";
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct X509FilePair {
|
||||
pub cert: PathBuf,
|
||||
pub key: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Settings {
|
||||
pub gameserver_address: SocketAddr,
|
||||
pub metrics_address: SocketAddr,
|
||||
pub auth_server_address: Option<String>,
|
||||
pub quic_files: Option<X509FilePair>,
|
||||
pub max_players: usize,
|
||||
pub world_seed: u32,
|
||||
//pub pvp_enabled: bool,
|
||||
@ -62,6 +69,7 @@ impl Default for Settings {
|
||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||
auth_server_address: Some("https://auth.veloren.net".into()),
|
||||
quic_files: None,
|
||||
world_seed: DEFAULT_WORLD_SEED,
|
||||
server_name: "Veloren Alpha".into(),
|
||||
max_players: 100,
|
||||
@ -140,6 +148,7 @@ impl Settings {
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
)),
|
||||
auth_server_address: None,
|
||||
quic_files: None,
|
||||
// If loading the default map file, make sure the seed is also default.
|
||||
world_seed: if load.map_file.is_some() {
|
||||
load.world_seed
|
||||
|
@ -31,12 +31,6 @@ pub enum Msg {
|
||||
Done(Result<Client, Error>),
|
||||
}
|
||||
|
||||
pub enum ClientConnArgs {
|
||||
Host(String),
|
||||
#[allow(dead_code)] //singleplayer
|
||||
Resolved(ConnectionArgs),
|
||||
}
|
||||
|
||||
pub struct AuthTrust(String, bool);
|
||||
|
||||
// Used to asynchronously parse the server address, resolve host names,
|
||||
@ -51,7 +45,7 @@ impl ClientInit {
|
||||
#[allow(clippy::op_ref)] // TODO: Pending review in #587
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn new(
|
||||
connection_args: ClientConnArgs,
|
||||
connection_args: ConnectionArgs,
|
||||
username: String,
|
||||
view_distance: Option<u32>,
|
||||
password: String,
|
||||
@ -89,18 +83,6 @@ impl ClientInit {
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
let connection_args = match connection_args {
|
||||
ClientConnArgs::Host(host) => match ConnectionArgs::resolve(&host, false).await {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
let _ = tx.send(Msg::Done(Err(Error::NoAddress)));
|
||||
tokio::task::block_in_place(move || drop(runtime2));
|
||||
return;
|
||||
},
|
||||
},
|
||||
ClientConnArgs::Resolved(r) => r,
|
||||
};
|
||||
|
||||
let mut last_err = None;
|
||||
|
||||
const FOUR_MINUTES_RETRIES: u64 = 48;
|
||||
|
@ -5,22 +5,18 @@ use super::char_selection::CharSelectionState;
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use crate::singleplayer::Singleplayer;
|
||||
use crate::{
|
||||
i18n::{Localization, LocalizationHandle},
|
||||
render::Renderer,
|
||||
settings::Settings,
|
||||
window::Event,
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
i18n::LocalizationHandle, render::Renderer, settings::Settings, window::Event, Direction,
|
||||
GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use client::addr::ConnectionArgs;
|
||||
use client::{
|
||||
addr::ConnectionArgs,
|
||||
error::{InitProtocolError, NetworkConnectError, NetworkError},
|
||||
ServerInfo,
|
||||
};
|
||||
use client_init::{ClientConnArgs, ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use common::comp;
|
||||
use common_base::span;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime;
|
||||
use tracing::error;
|
||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||
@ -78,7 +74,7 @@ impl PlayState for MainMenuState {
|
||||
&mut global_state.info_message,
|
||||
"singleplayer".to_owned(),
|
||||
"".to_owned(),
|
||||
ClientConnArgs::Resolved(ConnectionArgs::Mpsc(14004)),
|
||||
ConnectionArgs::Mpsc(14004),
|
||||
&mut self.client_init,
|
||||
Some(runtime),
|
||||
);
|
||||
@ -120,117 +116,14 @@ impl PlayState for MainMenuState {
|
||||
std::rc::Rc::new(std::cell::RefCell::new(client)),
|
||||
)));
|
||||
},
|
||||
Some(InitMsg::Done(Err(err))) => {
|
||||
let localized_strings = global_state.i18n.read();
|
||||
Some(InitMsg::Done(Err(e))) => {
|
||||
self.client_init = None;
|
||||
global_state.info_message = Some({
|
||||
let err = match err {
|
||||
InitError::NoAddress => {
|
||||
localized_strings.get("main.login.server_not_found").into()
|
||||
},
|
||||
InitError::ClientError {
|
||||
error,
|
||||
mismatched_server_info,
|
||||
} => match error {
|
||||
client::Error::SpecsErr(e) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.internal_error"),
|
||||
e
|
||||
),
|
||||
client::Error::AuthErr(e) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.authentication_error"),
|
||||
e
|
||||
),
|
||||
client::Error::Kicked(e) => e,
|
||||
client::Error::TooManyPlayers => {
|
||||
localized_strings.get("main.login.server_full").into()
|
||||
},
|
||||
client::Error::AuthServerNotTrusted => localized_strings
|
||||
.get("main.login.untrusted_auth_server")
|
||||
.into(),
|
||||
client::Error::ServerWentMad => localized_strings
|
||||
.get("main.login.outdated_client_or_server")
|
||||
.into(),
|
||||
client::Error::ServerTimeout => {
|
||||
localized_strings.get("main.login.timeout").into()
|
||||
},
|
||||
client::Error::ServerShutdown => {
|
||||
localized_strings.get("main.login.server_shut_down").into()
|
||||
},
|
||||
client::Error::NotOnWhitelist => {
|
||||
localized_strings.get("main.login.not_on_whitelist").into()
|
||||
},
|
||||
client::Error::Banned(reason) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.banned"),
|
||||
reason
|
||||
),
|
||||
client::Error::InvalidCharacter => {
|
||||
localized_strings.get("main.login.invalid_character").into()
|
||||
},
|
||||
client::Error::NetworkErr(NetworkError::ConnectFailed(
|
||||
NetworkConnectError::Handshake(InitProtocolError::WrongVersion(_)),
|
||||
)) => get_network_error_text(
|
||||
&localized_strings,
|
||||
localized_strings.get("main.login.network_wrong_version"),
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::NetworkErr(e) => get_network_error_text(
|
||||
&localized_strings,
|
||||
e,
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::ParticipantErr(e) => get_network_error_text(
|
||||
&localized_strings,
|
||||
e,
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::StreamErr(e) => get_network_error_text(
|
||||
&localized_strings,
|
||||
e,
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::Other(e) => {
|
||||
format!("{}: {}", localized_strings.get("common.error"), e)
|
||||
},
|
||||
client::Error::AuthClientError(e) => match e {
|
||||
// TODO: remove parentheses
|
||||
client::AuthClientError::RequestError(e) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.failed_sending_request"),
|
||||
e
|
||||
),
|
||||
client::AuthClientError::JsonError(e) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.failed_sending_request"),
|
||||
e
|
||||
),
|
||||
client::AuthClientError::InsecureSchema => localized_strings
|
||||
.get("main.login.insecure_auth_scheme")
|
||||
.into(),
|
||||
client::AuthClientError::ServerError(_, e) => {
|
||||
String::from_utf8_lossy(&e).to_string()
|
||||
},
|
||||
},
|
||||
client::Error::AuthServerUrlInvalid(e) => {
|
||||
format!(
|
||||
"{}: https://{}",
|
||||
localized_strings
|
||||
.get("main.login.failed_auth_server_url_invalid"),
|
||||
e
|
||||
)
|
||||
},
|
||||
},
|
||||
InitError::ClientCrashed => {
|
||||
localized_strings.get("main.login.client_crashed").into()
|
||||
},
|
||||
};
|
||||
// Log error for possible additional use later or incase that the error
|
||||
// displayed is cut of.
|
||||
error!("{}", err);
|
||||
err
|
||||
});
|
||||
tracing::trace!(?e, "raw Client Init error");
|
||||
let e = get_client_msg_error(e, &global_state.i18n);
|
||||
// Log error for possible additional use later or incase that the error
|
||||
// displayed is cut of.
|
||||
error!(?e, "Client Init failed");
|
||||
global_state.info_message = Some(e);
|
||||
},
|
||||
Some(InitMsg::IsAuthTrusted(auth_server)) => {
|
||||
if global_state
|
||||
@ -264,6 +157,7 @@ impl PlayState for MainMenuState {
|
||||
server_address,
|
||||
} => {
|
||||
let mut net_settings = &mut global_state.settings.networking;
|
||||
let use_quic = net_settings.use_quic;
|
||||
net_settings.username = username.clone();
|
||||
net_settings.default_server = server_address.clone();
|
||||
if !net_settings.servers.contains(&server_address) {
|
||||
@ -271,12 +165,23 @@ impl PlayState for MainMenuState {
|
||||
}
|
||||
global_state.settings.save_to_file_warn();
|
||||
|
||||
let connection_args = if use_quic {
|
||||
ConnectionArgs::Quic {
|
||||
hostname: server_address,
|
||||
prefer_ipv6: false,
|
||||
}
|
||||
} else {
|
||||
ConnectionArgs::Tcp {
|
||||
hostname: server_address,
|
||||
prefer_ipv6: false,
|
||||
}
|
||||
};
|
||||
attempt_login(
|
||||
&mut global_state.settings,
|
||||
&mut global_state.info_message,
|
||||
username,
|
||||
password,
|
||||
ClientConnArgs::Host(server_address),
|
||||
connection_args,
|
||||
&mut self.client_init,
|
||||
None,
|
||||
);
|
||||
@ -347,37 +252,115 @@ impl PlayState for MainMenuState {
|
||||
}
|
||||
}
|
||||
|
||||
/// When a network error is received and there is a mismatch between the client
|
||||
/// and server version it is almost definitely due to this mismatch rather than
|
||||
/// a true networking error.
|
||||
fn get_network_error_text(
|
||||
localization: &Localization,
|
||||
error: impl Debug,
|
||||
mismatched_server_info: Option<ServerInfo>,
|
||||
) -> String {
|
||||
if let Some(server_info) = mismatched_server_info {
|
||||
format!(
|
||||
"{} {}: {} {}: {}",
|
||||
localization.get("main.login.network_wrong_version"),
|
||||
localization.get("main.login.client_version"),
|
||||
common::util::GIT_HASH.to_string(),
|
||||
localization.get("main.login.server_version"),
|
||||
server_info.git_hash
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}: {:?}",
|
||||
localization.get("main.login.network_error"),
|
||||
error
|
||||
)
|
||||
fn get_client_msg_error(e: client_init::Error, localized_strings: &LocalizationHandle) -> String {
|
||||
let localization = localized_strings.read();
|
||||
|
||||
// When a network error is received and there is a mismatch between the client
|
||||
// and server version it is almost definitely due to this mismatch rather than
|
||||
// a true networking error.
|
||||
let net_e = |error: String, mismatched_server_info: Option<ServerInfo>| -> String {
|
||||
if let Some(server_info) = mismatched_server_info {
|
||||
format!(
|
||||
"{} {}: {} {}: {}",
|
||||
localization.get("main.login.network_wrong_version"),
|
||||
localization.get("main.login.client_version"),
|
||||
common::util::GIT_HASH.to_string(),
|
||||
localization.get("main.login.server_version"),
|
||||
server_info.git_hash
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}: {}",
|
||||
localization.get("main.login.network_error"),
|
||||
error
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
use client::Error;
|
||||
match e {
|
||||
InitError::NoAddress => localization.get("main.login.server_not_found").into(),
|
||||
InitError::ClientError {
|
||||
error,
|
||||
mismatched_server_info,
|
||||
} => match error {
|
||||
Error::SpecsErr(e) => {
|
||||
format!("{}: {}", localization.get("main.login.internal_error"), e)
|
||||
},
|
||||
Error::AuthErr(e) => format!(
|
||||
"{}: {}",
|
||||
localization.get("main.login.authentication_error"),
|
||||
e
|
||||
),
|
||||
Error::Kicked(e) => e,
|
||||
Error::TooManyPlayers => localization.get("main.login.server_full").into(),
|
||||
Error::AuthServerNotTrusted => {
|
||||
localization.get("main.login.untrusted_auth_server").into()
|
||||
},
|
||||
Error::ServerWentMad => localization
|
||||
.get("main.login.outdated_client_or_server")
|
||||
.into(),
|
||||
Error::ServerTimeout => localization.get("main.login.timeout").into(),
|
||||
Error::ServerShutdown => localization.get("main.login.server_shut_down").into(),
|
||||
Error::NotOnWhitelist => localization.get("main.login.not_on_whitelist").into(),
|
||||
Error::Banned(reason) => {
|
||||
format!("{}: {}", localization.get("main.login.banned"), reason)
|
||||
},
|
||||
Error::InvalidCharacter => localization.get("main.login.invalid_character").into(),
|
||||
Error::NetworkErr(NetworkError::ConnectFailed(NetworkConnectError::Handshake(
|
||||
InitProtocolError::WrongVersion(_),
|
||||
))) => net_e(
|
||||
localization
|
||||
.get("main.login.network_wrong_version")
|
||||
.to_owned(),
|
||||
mismatched_server_info,
|
||||
),
|
||||
Error::NetworkErr(e) => net_e(e.to_string(), mismatched_server_info),
|
||||
Error::ParticipantErr(e) => net_e(e.to_string(), mismatched_server_info),
|
||||
Error::StreamErr(e) => net_e(e.to_string(), mismatched_server_info),
|
||||
Error::HostnameLookupFailed(e) => {
|
||||
format!("{}: {}", localization.get("main.login.server_not_found"), e)
|
||||
},
|
||||
Error::Other(e) => {
|
||||
format!("{}: {}", localization.get("common.error"), e)
|
||||
},
|
||||
Error::AuthClientError(e) => match e {
|
||||
// TODO: remove parentheses
|
||||
client::AuthClientError::RequestError(e) => format!(
|
||||
"{}: {}",
|
||||
localization.get("main.login.failed_sending_request"),
|
||||
e
|
||||
),
|
||||
client::AuthClientError::JsonError(e) => format!(
|
||||
"{}: {}",
|
||||
localization.get("main.login.failed_sending_request"),
|
||||
e
|
||||
),
|
||||
client::AuthClientError::InsecureSchema => {
|
||||
localization.get("main.login.insecure_auth_scheme").into()
|
||||
},
|
||||
client::AuthClientError::ServerError(_, e) => {
|
||||
String::from_utf8_lossy(&e).to_string()
|
||||
},
|
||||
},
|
||||
Error::AuthServerUrlInvalid(e) => {
|
||||
format!(
|
||||
"{}: https://{}",
|
||||
localization.get("main.login.failed_auth_server_url_invalid"),
|
||||
e
|
||||
)
|
||||
},
|
||||
},
|
||||
InitError::ClientCrashed => localization.get("main.login.client_crashed").into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn attempt_login(
|
||||
settings: &mut Settings,
|
||||
info_message: &mut Option<String>,
|
||||
username: String,
|
||||
password: String,
|
||||
connection_args: ClientConnArgs,
|
||||
connection_args: ConnectionArgs,
|
||||
client_init: &mut Option<ClientInit>,
|
||||
runtime: Option<Arc<runtime::Runtime>>,
|
||||
) {
|
||||
|
@ -9,6 +9,7 @@ pub struct NetworkingSettings {
|
||||
pub servers: Vec<String>,
|
||||
pub default_server: String,
|
||||
pub trusted_auth_servers: HashSet<String>,
|
||||
pub use_quic: bool,
|
||||
}
|
||||
|
||||
impl Default for NetworkingSettings {
|
||||
@ -21,6 +22,7 @@ impl Default for NetworkingSettings {
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
use_quic: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user