mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added client/server version mismatch message when a network error is encountered during client init.
Added warning banner on character select when successfully connected to a server with a mismatched version.
This commit is contained in:
parent
8d664ded1a
commit
2e08c2f76f
@ -24,6 +24,7 @@
|
||||
"char_selection.eyeshape": "Eye Details",
|
||||
"char_selection.accessories": "Accessories",
|
||||
"char_selection.create_info_name": "Your Character needs a name!",
|
||||
"char_selection.version_mismatch": "WARNING! This server is running a different, possibly incompatible game version. Please update your game."
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
|
@ -50,7 +50,7 @@ https://veloren.net/account/."#,
|
||||
"main.login.timeout": "Timeout: Server did not respond in time. (Overloaded or network issues).",
|
||||
"main.login.server_shut_down": "Server shut down",
|
||||
"main.login.network_error": "Network error",
|
||||
"main.login.network_wrong_version": "The server is running a different version than you are. Check your version and update your game.",
|
||||
"main.login.network_wrong_version": "Mismatched server and client version, please update your game client.",
|
||||
"main.login.failed_sending_request": "Request to Auth server failed",
|
||||
"main.login.invalid_character": "The selected character is invalid",
|
||||
"main.login.client_crashed": "Client crashed",
|
||||
@ -58,7 +58,8 @@ https://veloren.net/account/."#,
|
||||
"main.login.banned": "You have been banned with the following reason",
|
||||
"main.login.kicked": "You have been kicked with the following reason",
|
||||
"main.login.select_language": "Select a language",
|
||||
|
||||
"main.login.client_version": "Client Version",
|
||||
"main.login.server_version": "Server Version",
|
||||
"main.servers.select_server": "Select a server",
|
||||
/// End Main screen section
|
||||
},
|
||||
|
@ -52,7 +52,7 @@ fn main() {
|
||||
let addr = ConnectionArgs::resolve(&server_addr, false)
|
||||
.await
|
||||
.expect("dns resolve failed");
|
||||
Client::new(addr, None, runtime2).await
|
||||
Client::new(addr, None, runtime2, &mut None).await
|
||||
})
|
||||
.expect("Failed to create client instance");
|
||||
|
||||
|
@ -59,7 +59,7 @@ pub fn make_client(runtime: &Arc<Runtime>, server: &str) -> Client {
|
||||
let connection_args = ConnectionArgs::resolve(server, false)
|
||||
.await
|
||||
.expect("DNS resolution failed");
|
||||
Client::new(connection_args, view_distance, runtime2)
|
||||
Client::new(connection_args, view_distance, runtime2, &mut None)
|
||||
.await
|
||||
.expect("Failed to connect to server")
|
||||
})
|
||||
|
@ -9,6 +9,7 @@ pub mod error;
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
pub use authc::AuthClientError;
|
||||
pub use common_net::msg::ServerInfo;
|
||||
pub use specs::{
|
||||
join::Join,
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
@ -49,7 +50,7 @@ use common_net::{
|
||||
world_msg::{EconomyInfo, SiteId, SiteInfo},
|
||||
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
||||
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
||||
PresenceKind, RegisterError, ServerGeneral, ServerInfo, ServerInit, ServerRegisterAnswer,
|
||||
PresenceKind, RegisterError, ServerGeneral, ServerInit, ServerRegisterAnswer,
|
||||
MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
sync::WorldSyncExt,
|
||||
@ -66,6 +67,7 @@ use rayon::prelude::*;
|
||||
use specs::Component;
|
||||
use std::{
|
||||
collections::{BTreeMap, VecDeque},
|
||||
mem,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@ -204,6 +206,8 @@ impl Client {
|
||||
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>,
|
||||
) -> Result<Self, Error> {
|
||||
let network = Network::new(Pid::new(), &runtime);
|
||||
|
||||
@ -235,8 +239,6 @@ impl Client {
|
||||
|
||||
register_stream.send(ClientType::Game)?;
|
||||
let server_info: ServerInfo = register_stream.recv().await?;
|
||||
|
||||
// TODO: Display that versions don't match in Voxygen
|
||||
if server_info.git_hash != *common::util::GIT_HASH {
|
||||
warn!(
|
||||
"Server is running {}[{}], you are running {}[{}], versions might be incompatible!",
|
||||
@ -245,6 +247,10 @@ impl Client {
|
||||
common::util::GIT_HASH.to_string(),
|
||||
common::util::GIT_DATE.to_string(),
|
||||
);
|
||||
|
||||
// Pass the server info back to the caller to ensure they can access it even
|
||||
// if this function errors.
|
||||
mem::swap(mismatched_server_info, &mut Some(server_info.clone()));
|
||||
}
|
||||
debug!("Auth Server: {:?}", server_info.auth_provider);
|
||||
|
||||
@ -2447,6 +2453,7 @@ mod tests {
|
||||
ConnectionArgs::IpAndPort(vec![socket]),
|
||||
view_distance,
|
||||
runtime2,
|
||||
&mut None,
|
||||
));
|
||||
|
||||
let _ = veloren_client.map(|mut client| {
|
||||
|
@ -1055,7 +1055,7 @@ where
|
||||
handle.spawn(async move {
|
||||
match finished_receiver.await {
|
||||
Ok(data) => f(data),
|
||||
Err(e) => panic!("{}{}: {}", name, CHANNEL_ERR, e),
|
||||
Err(e) => error!("{}{}: {}", name, CHANNEL_ERR, e),
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
||||
},
|
||||
window, GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use client::{Client, ServerInfo};
|
||||
use common::{
|
||||
assets::AssetHandle,
|
||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
||||
@ -223,7 +223,7 @@ struct Controls {
|
||||
version: String,
|
||||
// Alpha disclaimer
|
||||
alpha: String,
|
||||
|
||||
server_mismatched_version: Option<String>,
|
||||
tooltip_manager: TooltipManager,
|
||||
// Zone for rotating the character with the mouse
|
||||
mouse_detector: mouse_detector::State,
|
||||
@ -264,16 +264,25 @@ enum Message {
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
fn new(fonts: Fonts, imgs: Imgs, selected: Option<CharacterId>, default_name: String) -> Self {
|
||||
fn new(
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
selected: Option<CharacterId>,
|
||||
default_name: String,
|
||||
server_info: &ServerInfo,
|
||||
) -> Self {
|
||||
let version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||
let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str());
|
||||
let server_mismatched_version = (common::util::GIT_HASH.to_string()
|
||||
!= server_info.git_hash)
|
||||
.then(|| server_info.git_hash.clone());
|
||||
|
||||
Self {
|
||||
fonts,
|
||||
imgs,
|
||||
version,
|
||||
alpha,
|
||||
|
||||
server_mismatched_version,
|
||||
tooltip_manager: TooltipManager::new(TOOLTIP_HOVER_DUR, TOOLTIP_FADE_DUR),
|
||||
mouse_detector: Default::default(),
|
||||
mode: Mode::select(Some(InfoContent::LoadingCharacters)),
|
||||
@ -1210,14 +1219,46 @@ impl Controls {
|
||||
},
|
||||
};
|
||||
|
||||
Container::new(
|
||||
Column::with_children(vec![top_text.into(), content])
|
||||
.spacing(3)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.padding(3)
|
||||
.into()
|
||||
// TODO: There is probably a better way to conditionally add in the warning box
|
||||
// here
|
||||
if let Some(mismatched_version) = &self.server_mismatched_version {
|
||||
let warning = iced::Text::<IcedRenderer>::new(format!(
|
||||
"{}\n{}: {} {}: {}",
|
||||
i18n.get("char_selection.version_mismatch"),
|
||||
i18n.get("main.login.server_version"),
|
||||
mismatched_version,
|
||||
i18n.get("main.login.client_version"),
|
||||
common::util::GIT_HASH.to_string()
|
||||
))
|
||||
.size(self.fonts.cyri.scale(18))
|
||||
.color(iced::Color::from_rgb(1.0, 0.0, 0.0))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center);
|
||||
let warning_container =
|
||||
Container::new(Row::with_children(vec![warning.into()]).width(Length::Fill))
|
||||
.style(style::container::Style::color(Rgba::new(0, 0, 0, 217)))
|
||||
.padding(12)
|
||||
.center_x()
|
||||
.width(Length::Fill);
|
||||
|
||||
Container::new(
|
||||
Column::with_children(vec![top_text.into(), warning_container.into(), content])
|
||||
.spacing(3)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.padding(3)
|
||||
.into()
|
||||
} else {
|
||||
Container::new(
|
||||
Column::with_children(vec![top_text.into(), content])
|
||||
.spacing(3)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.padding(3)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
|
||||
@ -1451,6 +1492,7 @@ impl CharSelectionUi {
|
||||
Imgs::load(&mut ui).expect("Failed to load images"),
|
||||
selected_character,
|
||||
default_name,
|
||||
client.server_info(),
|
||||
);
|
||||
|
||||
Self {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use client::{
|
||||
addr::ConnectionArgs,
|
||||
error::{Error as ClientError, NetworkConnectError, NetworkError},
|
||||
Client,
|
||||
Client, ServerInfo,
|
||||
};
|
||||
use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||
use std::{
|
||||
@ -17,7 +17,10 @@ use tracing::{trace, warn};
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NoAddress,
|
||||
ClientError(ClientError),
|
||||
ClientError {
|
||||
error: ClientError,
|
||||
mismatched_server_info: Option<ServerInfo>,
|
||||
},
|
||||
ClientCrashed,
|
||||
}
|
||||
|
||||
@ -103,16 +106,21 @@ impl ClientInit {
|
||||
if cancel2.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let mut mismatched_server_info = None;
|
||||
match Client::new(
|
||||
connection_args.clone(),
|
||||
view_distance,
|
||||
Arc::clone(&runtime2),
|
||||
&mut mismatched_server_info,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mut client) => {
|
||||
if let Err(e) = client.register(username, password, trust_fn).await {
|
||||
last_err = Some(Error::ClientError(e));
|
||||
last_err = Some(Error::ClientError {
|
||||
error: e,
|
||||
mismatched_server_info: None,
|
||||
});
|
||||
break 'tries;
|
||||
}
|
||||
let _ = tx.send(Msg::Done(Ok(client)));
|
||||
@ -126,7 +134,10 @@ impl ClientInit {
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(?e, "Aborting server connection attempt");
|
||||
last_err = Some(Error::ClientError(e));
|
||||
last_err = Some(Error::ClientError {
|
||||
error: e,
|
||||
mismatched_server_info,
|
||||
});
|
||||
break 'tries;
|
||||
},
|
||||
}
|
||||
|
@ -13,11 +13,14 @@ use crate::{
|
||||
};
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use client::addr::ConnectionArgs;
|
||||
use client::error::{InitProtocolError, NetworkConnectError, NetworkError};
|
||||
use client::{
|
||||
error::{InitProtocolError, NetworkConnectError, NetworkError},
|
||||
ServerInfo,
|
||||
};
|
||||
use client_init::{ClientConnArgs, ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use common::{assets::AssetExt, comp};
|
||||
use common_base::span;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt::Debug, sync::Arc};
|
||||
use tokio::runtime;
|
||||
use tracing::error;
|
||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||
@ -125,7 +128,10 @@ impl PlayState for MainMenuState {
|
||||
InitError::NoAddress => {
|
||||
localized_strings.get("main.login.server_not_found").into()
|
||||
},
|
||||
InitError::ClientError(err) => match err {
|
||||
InitError::ClientError {
|
||||
error,
|
||||
mismatched_server_info,
|
||||
} => match error {
|
||||
client::Error::SpecsErr(e) => format!(
|
||||
"{}: {}",
|
||||
localized_strings.get("main.login.internal_error"),
|
||||
@ -165,23 +171,25 @@ impl PlayState for MainMenuState {
|
||||
},
|
||||
client::Error::NetworkErr(NetworkError::ConnectFailed(
|
||||
NetworkConnectError::Handshake(InitProtocolError::WrongVersion(_)),
|
||||
)) => localized_strings
|
||||
.get("main.login.network_wrong_version")
|
||||
.into(),
|
||||
client::Error::NetworkErr(e) => format!(
|
||||
"{}: {:?}",
|
||||
localized_strings.get("main.login.network_error"),
|
||||
e
|
||||
)) => get_network_error_text(
|
||||
&localized_strings,
|
||||
localized_strings.get("main.login.network_wrong_version"),
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::ParticipantErr(e) => format!(
|
||||
"{}: {:?}",
|
||||
localized_strings.get("main.login.network_error"),
|
||||
e
|
||||
client::Error::NetworkErr(e) => get_network_error_text(
|
||||
&localized_strings,
|
||||
e,
|
||||
mismatched_server_info,
|
||||
),
|
||||
client::Error::StreamErr(e) => format!(
|
||||
"{}: {:?}",
|
||||
localized_strings.get("main.login.network_error"),
|
||||
e
|
||||
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)
|
||||
@ -336,6 +344,31 @@ 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 attempt_login(
|
||||
settings: &mut Settings,
|
||||
info_message: &mut Option<String>,
|
||||
|
Loading…
Reference in New Issue
Block a user