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
53025a861e
commit
76daf7a395
@ -24,6 +24,7 @@
|
|||||||
"char_selection.eyeshape": "Eye Details",
|
"char_selection.eyeshape": "Eye Details",
|
||||||
"char_selection.accessories": "Accessories",
|
"char_selection.accessories": "Accessories",
|
||||||
"char_selection.create_info_name": "Your Character needs a name!",
|
"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: {
|
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.timeout": "Timeout: Server did not respond in time. (Overloaded or network issues).",
|
||||||
"main.login.server_shut_down": "Server shut down",
|
"main.login.server_shut_down": "Server shut down",
|
||||||
"main.login.network_error": "Network error",
|
"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.failed_sending_request": "Request to Auth server failed",
|
||||||
"main.login.invalid_character": "The selected character is invalid",
|
"main.login.invalid_character": "The selected character is invalid",
|
||||||
"main.login.client_crashed": "Client crashed",
|
"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.banned": "You have been banned with the following reason",
|
||||||
"main.login.kicked": "You have been kicked with the following reason",
|
"main.login.kicked": "You have been kicked with the following reason",
|
||||||
"main.login.select_language": "Select a language",
|
"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",
|
"main.servers.select_server": "Select a server",
|
||||||
/// End Main screen section
|
/// End Main screen section
|
||||||
},
|
},
|
||||||
|
@ -52,7 +52,7 @@ fn main() {
|
|||||||
let addr = ConnectionArgs::resolve(&server_addr, false)
|
let addr = ConnectionArgs::resolve(&server_addr, false)
|
||||||
.await
|
.await
|
||||||
.expect("dns resolve failed");
|
.expect("dns resolve failed");
|
||||||
Client::new(addr, None, runtime2).await
|
Client::new(addr, None, runtime2, &mut None).await
|
||||||
})
|
})
|
||||||
.expect("Failed to create client instance");
|
.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)
|
let connection_args = ConnectionArgs::resolve(server, false)
|
||||||
.await
|
.await
|
||||||
.expect("DNS resolution failed");
|
.expect("DNS resolution failed");
|
||||||
Client::new(connection_args, view_distance, runtime2)
|
Client::new(connection_args, view_distance, runtime2, &mut None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to server")
|
.expect("Failed to connect to server")
|
||||||
})
|
})
|
||||||
|
@ -9,6 +9,7 @@ pub mod error;
|
|||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
pub use authc::AuthClientError;
|
pub use authc::AuthClientError;
|
||||||
|
pub use common_net::msg::ServerInfo;
|
||||||
pub use specs::{
|
pub use specs::{
|
||||||
join::Join,
|
join::Join,
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
@ -49,7 +50,7 @@ use common_net::{
|
|||||||
world_msg::{EconomyInfo, SiteId, SiteInfo},
|
world_msg::{EconomyInfo, SiteId, SiteInfo},
|
||||||
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, ClientType,
|
||||||
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, PlayerListUpdate,
|
||||||
PresenceKind, RegisterError, ServerGeneral, ServerInfo, ServerInit, ServerRegisterAnswer,
|
PresenceKind, RegisterError, ServerGeneral, ServerInit, ServerRegisterAnswer,
|
||||||
MAX_BYTES_CHAT_MSG,
|
MAX_BYTES_CHAT_MSG,
|
||||||
},
|
},
|
||||||
sync::WorldSyncExt,
|
sync::WorldSyncExt,
|
||||||
@ -66,6 +67,7 @@ use rayon::prelude::*;
|
|||||||
use specs::Component;
|
use specs::Component;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, VecDeque},
|
collections::{BTreeMap, VecDeque},
|
||||||
|
mem,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
@ -204,6 +206,8 @@ impl Client {
|
|||||||
addr: ConnectionArgs,
|
addr: ConnectionArgs,
|
||||||
view_distance: Option<u32>,
|
view_distance: Option<u32>,
|
||||||
runtime: Arc<Runtime>,
|
runtime: Arc<Runtime>,
|
||||||
|
// TODO: refactor to avoid needing to use this out parameter
|
||||||
|
mismatched_server_info: &mut Option<ServerInfo>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let network = Network::new(Pid::new(), &runtime);
|
let network = Network::new(Pid::new(), &runtime);
|
||||||
|
|
||||||
@ -235,8 +239,6 @@ impl Client {
|
|||||||
|
|
||||||
register_stream.send(ClientType::Game)?;
|
register_stream.send(ClientType::Game)?;
|
||||||
let server_info: ServerInfo = register_stream.recv().await?;
|
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 {
|
if server_info.git_hash != *common::util::GIT_HASH {
|
||||||
warn!(
|
warn!(
|
||||||
"Server is running {}[{}], you are running {}[{}], versions might be incompatible!",
|
"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_HASH.to_string(),
|
||||||
common::util::GIT_DATE.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);
|
debug!("Auth Server: {:?}", server_info.auth_provider);
|
||||||
|
|
||||||
@ -2449,6 +2455,7 @@ mod tests {
|
|||||||
ConnectionArgs::IpAndPort(vec![socket]),
|
ConnectionArgs::IpAndPort(vec![socket]),
|
||||||
view_distance,
|
view_distance,
|
||||||
runtime2,
|
runtime2,
|
||||||
|
&mut None,
|
||||||
));
|
));
|
||||||
|
|
||||||
let _ = veloren_client.map(|mut client| {
|
let _ = veloren_client.map(|mut client| {
|
||||||
|
@ -1055,7 +1055,7 @@ where
|
|||||||
handle.spawn(async move {
|
handle.spawn(async move {
|
||||||
match finished_receiver.await {
|
match finished_receiver.await {
|
||||||
Ok(data) => f(data),
|
Ok(data) => f(data),
|
||||||
Err(e) => panic!("{}{}: {}", name, CHANNEL_ERR, e),
|
Err(e) => error!("{}{}: {}", name, CHANNEL_ERR, e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
window, GlobalState,
|
window, GlobalState,
|
||||||
};
|
};
|
||||||
use client::Client;
|
use client::{Client, ServerInfo};
|
||||||
use common::{
|
use common::{
|
||||||
assets::AssetHandle,
|
assets::AssetHandle,
|
||||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER, MAX_NAME_LENGTH},
|
||||||
@ -223,7 +223,7 @@ struct Controls {
|
|||||||
version: String,
|
version: String,
|
||||||
// Alpha disclaimer
|
// Alpha disclaimer
|
||||||
alpha: String,
|
alpha: String,
|
||||||
|
server_mismatched_version: Option<String>,
|
||||||
tooltip_manager: TooltipManager,
|
tooltip_manager: TooltipManager,
|
||||||
// Zone for rotating the character with the mouse
|
// Zone for rotating the character with the mouse
|
||||||
mouse_detector: mouse_detector::State,
|
mouse_detector: mouse_detector::State,
|
||||||
@ -264,16 +264,25 @@ enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Controls {
|
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 version = common::util::DISPLAY_VERSION_LONG.clone();
|
||||||
let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str());
|
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 {
|
Self {
|
||||||
fonts,
|
fonts,
|
||||||
imgs,
|
imgs,
|
||||||
version,
|
version,
|
||||||
alpha,
|
alpha,
|
||||||
|
server_mismatched_version,
|
||||||
tooltip_manager: TooltipManager::new(TOOLTIP_HOVER_DUR, TOOLTIP_FADE_DUR),
|
tooltip_manager: TooltipManager::new(TOOLTIP_HOVER_DUR, TOOLTIP_FADE_DUR),
|
||||||
mouse_detector: Default::default(),
|
mouse_detector: Default::default(),
|
||||||
mode: Mode::select(Some(InfoContent::LoadingCharacters)),
|
mode: Mode::select(Some(InfoContent::LoadingCharacters)),
|
||||||
@ -1210,14 +1219,46 @@ impl Controls {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Container::new(
|
// TODO: There is probably a better way to conditionally add in the warning box
|
||||||
Column::with_children(vec![top_text.into(), content])
|
// here
|
||||||
.spacing(3)
|
if let Some(mismatched_version) = &self.server_mismatched_version {
|
||||||
.width(Length::Fill)
|
let warning = iced::Text::<IcedRenderer>::new(format!(
|
||||||
.height(Length::Fill),
|
"{}\n{}: {} {}: {}",
|
||||||
)
|
i18n.get("char_selection.version_mismatch"),
|
||||||
.padding(3)
|
i18n.get("main.login.server_version"),
|
||||||
.into()
|
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]) {
|
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"),
|
Imgs::load(&mut ui).expect("Failed to load images"),
|
||||||
selected_character,
|
selected_character,
|
||||||
default_name,
|
default_name,
|
||||||
|
client.server_info(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use client::{
|
use client::{
|
||||||
addr::ConnectionArgs,
|
addr::ConnectionArgs,
|
||||||
error::{Error as ClientError, NetworkConnectError, NetworkError},
|
error::{Error as ClientError, NetworkConnectError, NetworkError},
|
||||||
Client,
|
Client, ServerInfo,
|
||||||
};
|
};
|
||||||
use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError};
|
use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||||
use std::{
|
use std::{
|
||||||
@ -17,7 +17,10 @@ use tracing::{trace, warn};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
NoAddress,
|
NoAddress,
|
||||||
ClientError(ClientError),
|
ClientError {
|
||||||
|
error: ClientError,
|
||||||
|
mismatched_server_info: Option<ServerInfo>,
|
||||||
|
},
|
||||||
ClientCrashed,
|
ClientCrashed,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,16 +106,21 @@ impl ClientInit {
|
|||||||
if cancel2.load(Ordering::Relaxed) {
|
if cancel2.load(Ordering::Relaxed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let mut mismatched_server_info = None;
|
||||||
match Client::new(
|
match Client::new(
|
||||||
connection_args.clone(),
|
connection_args.clone(),
|
||||||
view_distance,
|
view_distance,
|
||||||
Arc::clone(&runtime2),
|
Arc::clone(&runtime2),
|
||||||
|
&mut mismatched_server_info,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut client) => {
|
Ok(mut client) => {
|
||||||
if let Err(e) = client.register(username, password, trust_fn).await {
|
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;
|
break 'tries;
|
||||||
}
|
}
|
||||||
let _ = tx.send(Msg::Done(Ok(client)));
|
let _ = tx.send(Msg::Done(Ok(client)));
|
||||||
@ -126,7 +134,10 @@ impl ClientInit {
|
|||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!(?e, "Aborting server connection attempt");
|
trace!(?e, "Aborting server connection attempt");
|
||||||
last_err = Some(Error::ClientError(e));
|
last_err = Some(Error::ClientError {
|
||||||
|
error: e,
|
||||||
|
mismatched_server_info,
|
||||||
|
});
|
||||||
break 'tries;
|
break 'tries;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,15 @@ use crate::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
use client::addr::ConnectionArgs;
|
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 client_init::{ClientConnArgs, ClientInit, Error as InitError, Msg as InitMsg};
|
||||||
use common::{assets::AssetExt, comp};
|
use common::{assets::AssetExt, comp};
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
use scene::Scene;
|
use scene::Scene;
|
||||||
use std::sync::Arc;
|
use std::{fmt::Debug, sync::Arc};
|
||||||
use tokio::runtime;
|
use tokio::runtime;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||||
@ -129,7 +132,10 @@ impl PlayState for MainMenuState {
|
|||||||
InitError::NoAddress => {
|
InitError::NoAddress => {
|
||||||
localized_strings.get("main.login.server_not_found").into()
|
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!(
|
client::Error::SpecsErr(e) => format!(
|
||||||
"{}: {}",
|
"{}: {}",
|
||||||
localized_strings.get("main.login.internal_error"),
|
localized_strings.get("main.login.internal_error"),
|
||||||
@ -169,23 +175,25 @@ impl PlayState for MainMenuState {
|
|||||||
},
|
},
|
||||||
client::Error::NetworkErr(NetworkError::ConnectFailed(
|
client::Error::NetworkErr(NetworkError::ConnectFailed(
|
||||||
NetworkConnectError::Handshake(InitProtocolError::WrongVersion(_)),
|
NetworkConnectError::Handshake(InitProtocolError::WrongVersion(_)),
|
||||||
)) => localized_strings
|
)) => get_network_error_text(
|
||||||
.get("main.login.network_wrong_version")
|
&localized_strings,
|
||||||
.into(),
|
localized_strings.get("main.login.network_wrong_version"),
|
||||||
client::Error::NetworkErr(e) => format!(
|
mismatched_server_info,
|
||||||
"{}: {:?}",
|
|
||||||
localized_strings.get("main.login.network_error"),
|
|
||||||
e
|
|
||||||
),
|
),
|
||||||
client::Error::ParticipantErr(e) => format!(
|
client::Error::NetworkErr(e) => get_network_error_text(
|
||||||
"{}: {:?}",
|
&localized_strings,
|
||||||
localized_strings.get("main.login.network_error"),
|
e,
|
||||||
e
|
mismatched_server_info,
|
||||||
),
|
),
|
||||||
client::Error::StreamErr(e) => format!(
|
client::Error::ParticipantErr(e) => get_network_error_text(
|
||||||
"{}: {:?}",
|
&localized_strings,
|
||||||
localized_strings.get("main.login.network_error"),
|
e,
|
||||||
e
|
mismatched_server_info,
|
||||||
|
),
|
||||||
|
client::Error::StreamErr(e) => get_network_error_text(
|
||||||
|
&localized_strings,
|
||||||
|
e,
|
||||||
|
mismatched_server_info,
|
||||||
),
|
),
|
||||||
client::Error::Other(e) => {
|
client::Error::Other(e) => {
|
||||||
format!("{}: {}", localized_strings.get("common.error"), e)
|
format!("{}: {}", localized_strings.get("common.error"), e)
|
||||||
@ -349,6 +357,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(
|
fn attempt_login(
|
||||||
settings: &mut Settings,
|
settings: &mut Settings,
|
||||||
info_message: &mut Option<String>,
|
info_message: &mut Option<String>,
|
||||||
|
Loading…
Reference in New Issue
Block a user