mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Revert "Merge branch 'sharp/revert-quinn' into 'master'"
This reverts merge request !2315
This commit is contained in:
parent
88ba97b35a
commit
99304ffddd
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5483,6 +5483,7 @@ dependencies = [
|
||||
"hashbrown",
|
||||
"image",
|
||||
"num 0.4.0",
|
||||
"quinn",
|
||||
"rayon",
|
||||
"ron",
|
||||
"rustyline",
|
||||
@ -5752,6 +5753,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(Error::HostnameLookupFailed)?
|
||||
{
|
||||
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)]
|
||||
@ -54,85 +87,47 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_localhost() {
|
||||
let args = ConnectionArgs::resolve("localhost", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
let args = resolve("localhost", false).await.expect("resolve failed");
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
|
||||
let args = ConnectionArgs::resolve("localhost:666", false)
|
||||
let args = resolve("localhost:666", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].port(), 666);
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].port(), 666);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_ipv6() {
|
||||
let args = ConnectionArgs::resolve("localhost", true)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].ip(), Ipv6Addr::LOCALHOST);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
let args = resolve("localhost", true).await.expect("resolve failed");
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].ip(), Ipv6Addr::LOCALHOST);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve() {
|
||||
let args = ConnectionArgs::resolve("google.com", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
async fn tresolve() {
|
||||
let args = resolve("google.com", false).await.expect("resolve failed");
|
||||
assert!(args.len() == 1 || args.len() == 2);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
|
||||
let args = ConnectionArgs::resolve("127.0.0.1", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
let args = resolve("127.0.0.1", false).await.expect("resolve failed");
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
|
||||
let args = ConnectionArgs::resolve("55.66.77.88", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::new(55, 66, 77, 88)));
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
let args = resolve("55.66.77.88", false).await.expect("resolve failed");
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 14004);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::new(55, 66, 77, 88)));
|
||||
|
||||
let args = ConnectionArgs::resolve("127.0.0.1:776", false)
|
||||
let args = resolve("127.0.0.1:776", false)
|
||||
.await
|
||||
.expect("resolve failed");
|
||||
if let ConnectionArgs::IpAndPort(args) = args {
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 776);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
} else {
|
||||
panic!("wrong resolution");
|
||||
}
|
||||
assert_eq!(args.len(), 1);
|
||||
assert_eq!(args[0].port(), 776);
|
||||
assert_eq!(args[0].ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ pub enum Error {
|
||||
NetworkErr(NetworkError),
|
||||
ParticipantErr(ParticipantError),
|
||||
StreamErr(StreamError),
|
||||
ServerWentMad,
|
||||
ServerTimeout,
|
||||
ServerShutdown,
|
||||
TooManyPlayers,
|
||||
@ -18,6 +17,7 @@ pub enum Error {
|
||||
AuthClientError(AuthClientError),
|
||||
AuthServerUrlInvalid(String),
|
||||
AuthServerNotTrusted,
|
||||
HostnameLookupFailed(std::io::Error),
|
||||
Banned(String),
|
||||
/// Persisted character data is invalid or missing
|
||||
InvalidCharacter,
|
||||
|
@ -203,10 +203,8 @@ pub struct CharacterList {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new `Client`.
|
||||
pub async fn new(
|
||||
addr: ConnectionArgs,
|
||||
view_distance: Option<u32>,
|
||||
runtime: Arc<Runtime>,
|
||||
// TODO: refactor to avoid needing to use this out parameter
|
||||
mismatched_server_info: &mut Option<ServerInfo>,
|
||||
@ -214,20 +212,21 @@ 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, ConnectAddr::Tcp).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?,
|
||||
};
|
||||
@ -694,7 +693,7 @@ impl Client {
|
||||
|
||||
tick: 0,
|
||||
state,
|
||||
view_distance,
|
||||
view_distance: None,
|
||||
loaded_distance: 0.0,
|
||||
|
||||
pending_chunks: HashMap::new(),
|
||||
@ -2447,7 +2446,6 @@ impl Drop for Client {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[test]
|
||||
/// THIS TEST VERIFIES THE CONSTANT API.
|
||||
@ -2457,17 +2455,16 @@ mod tests {
|
||||
/// CONTACT @Core Developer BEFORE MERGING CHANGES TO THIS TEST
|
||||
fn constant_api_test() {
|
||||
use common::clock::Clock;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
const SPT: f64 = 1.0 / 60.0;
|
||||
|
||||
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9000);
|
||||
let view_distance: Option<u32> = None;
|
||||
let runtime = Arc::new(Runtime::new().unwrap());
|
||||
let runtime2 = Arc::clone(&runtime);
|
||||
let veloren_client: Result<Client, Error> = runtime.block_on(Client::new(
|
||||
ConnectionArgs::IpAndPort(vec![socket]),
|
||||
view_distance,
|
||||
ConnectionArgs::Tcp {
|
||||
hostname: "127.0.0.1:9000".to_owned(),
|
||||
prefer_ipv6: false,
|
||||
},
|
||||
runtime2,
|
||||
&mut None,
|
||||
));
|
||||
|
@ -59,6 +59,7 @@ where
|
||||
.add_directive("tokio_util=info".parse().unwrap())
|
||||
.add_directive("rustls=info".parse().unwrap())
|
||||
.add_directive("veloren_network_protocol=info".parse().unwrap())
|
||||
.add_directive("quinn_proto::connection=info".parse().unwrap())
|
||||
.add_directive(
|
||||
"veloren_server::persistence::character=info"
|
||||
.parse()
|
||||
|
@ -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
|
||||
|
@ -116,6 +116,10 @@ impl PlayState for CharSelectionState {
|
||||
},
|
||||
ui::Event::Play(character_id) => {
|
||||
self.client.borrow_mut().request_character(character_id);
|
||||
//Send our ViewDistance
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.set_view_distance(global_state.settings.graphics.view_distance);
|
||||
|
||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||
global_state,
|
||||
|
@ -17,7 +17,6 @@ use tracing::{trace, warn};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NoAddress,
|
||||
ClientError {
|
||||
error: ClientError,
|
||||
mismatched_server_info: Option<ServerInfo>,
|
||||
@ -31,12 +30,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,9 +44,8 @@ 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,
|
||||
runtime: Option<Arc<runtime::Runtime>>,
|
||||
) -> Self {
|
||||
@ -89,18 +81,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;
|
||||
@ -111,7 +91,6 @@ impl ClientInit {
|
||||
let mut mismatched_server_info = None;
|
||||
match Client::new(
|
||||
connection_args.clone(),
|
||||
view_distance,
|
||||
Arc::clone(&runtime2),
|
||||
&mut mismatched_server_info,
|
||||
)
|
||||
@ -146,8 +125,11 @@ impl ClientInit {
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
|
||||
// Parsing/host name resolution successful but no connection succeeded.
|
||||
let _ = tx.send(Msg::Done(Err(last_err.unwrap_or(Error::NoAddress))));
|
||||
// Only possibility for no last_err is aborting
|
||||
let _ = tx.send(Msg::Done(Err(last_err.unwrap_or(Error::ClientError {
|
||||
error: ClientError::Other("Connection attempt aborted by user".to_owned()),
|
||||
mismatched_server_info: None,
|
||||
}))));
|
||||
|
||||
// Safe drop runtime
|
||||
tokio::task::block_in_place(move || drop(runtime2));
|
||||
|
@ -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};
|
||||
@ -74,11 +70,10 @@ impl PlayState for MainMenuState {
|
||||
Ok(Ok(runtime)) => {
|
||||
// Attempt login after the server is finished initializing
|
||||
attempt_login(
|
||||
&mut global_state.settings,
|
||||
&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 +115,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 +156,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 +164,22 @@ 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 +250,110 @@ 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::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::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>>,
|
||||
) {
|
||||
@ -391,7 +367,6 @@ fn attempt_login(
|
||||
*client_init = Some(ClientInit::new(
|
||||
connection_args,
|
||||
username,
|
||||
Some(settings.graphics.view_distance),
|
||||
password,
|
||||
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