diff --git a/Cargo.lock b/Cargo.lock
index 50e3df519a..5995e4cc28 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/client/Cargo.toml b/client/Cargo.toml
index e0113e12f2..02861fba46 100644
--- a/client/Cargo.toml
+++ b/client/Cargo.toml
@@ -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 }
diff --git a/client/src/addr.rs b/client/src/addr.rs
index fa8c871aea..6002ce4a4c 100644
--- a/client/src/addr.rs
+++ b/client/src/addr.rs
@@ -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)]
diff --git a/client/src/error.rs b/client/src/error.rs
index a9079d77d6..c5021ec2e5 100644
--- a/client/src/error.rs
+++ b/client/src/error.rs
@@ -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,
diff --git a/client/src/lib.rs b/client/src/lib.rs
index 0d04801bb5..aaf0a342b9 100644
--- a/client/src/lib.rs
+++ b/client/src/lib.rs
@@ -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?,
         };
diff --git a/server/Cargo.toml b/server/Cargo.toml
index e6c61d8516..4f24502bd5 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -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"
diff --git a/server/src/lib.rs b/server/src/lib.rs
index eba8a14299..c773a4e14c 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -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
diff --git a/server/src/settings.rs b/server/src/settings.rs
index 1424af9fe5..5c819381d1 100644
--- a/server/src/settings.rs
+++ b/server/src/settings.rs
@@ -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
diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs
index de69abc5a5..5decad8a2d 100644
--- a/voxygen/src/menu/main/client_init.rs
+++ b/voxygen/src/menu/main/client_init.rs
@@ -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;
diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs
index dac729c725..b92a2d9171 100644
--- a/voxygen/src/menu/main/mod.rs
+++ b/voxygen/src/menu/main/mod.rs
@@ -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>>,
 ) {
diff --git a/voxygen/src/settings/networking.rs b/voxygen/src/settings/networking.rs
index 4732b5dc7e..402fb5f4e4 100644
--- a/voxygen/src/settings/networking.rs
+++ b/voxygen/src/settings/networking.rs
@@ -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,
         }
     }
 }