mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
728bff610b
only `localhost` are allowed in a release build. when debug assertions are on, others are also allowed. This change undoes the changes to the settings, so compared to master, there is no effect
362 lines
15 KiB
Rust
362 lines
15 KiB
Rust
mod client_init;
|
|
mod ui;
|
|
|
|
use super::char_selection::CharSelectionState;
|
|
#[cfg(feature = "singleplayer")]
|
|
use crate::singleplayer::Singleplayer;
|
|
use crate::{
|
|
i18n::{i18n_asset_key, Localization},
|
|
render::Renderer,
|
|
settings::Settings,
|
|
window::Event,
|
|
Direction, GlobalState, PlayState, PlayStateResult,
|
|
};
|
|
use client::{
|
|
addr::ConnectionArgs,
|
|
error::{InitProtocolError, NetworkConnectError, NetworkError},
|
|
};
|
|
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 tokio::runtime;
|
|
use tracing::error;
|
|
use ui::{Event as MainMenuEvent, MainMenuUi};
|
|
|
|
pub struct MainMenuState {
|
|
main_menu_ui: MainMenuUi,
|
|
// Used for client creation.
|
|
client_init: Option<ClientInit>,
|
|
}
|
|
|
|
impl MainMenuState {
|
|
/// Create a new `MainMenuState`.
|
|
pub fn new(global_state: &mut GlobalState) -> Self {
|
|
Self {
|
|
main_menu_ui: MainMenuUi::new(global_state),
|
|
client_init: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PlayState for MainMenuState {
|
|
fn enter(&mut self, global_state: &mut GlobalState, _: Direction) {
|
|
// Kick off title music
|
|
if global_state.settings.audio.output.is_enabled() && global_state.audio.music_enabled() {
|
|
global_state.audio.play_title_music();
|
|
}
|
|
|
|
// Reset singleplayer server if it was running already
|
|
#[cfg(feature = "singleplayer")]
|
|
{
|
|
global_state.singleplayer = None;
|
|
}
|
|
|
|
// Updated localization in case the selected language was changed
|
|
self.main_menu_ui
|
|
.update_language(global_state.i18n, &global_state.settings);
|
|
// Set scale mode in case it was change
|
|
self.main_menu_ui
|
|
.set_scale_mode(global_state.settings.gameplay.ui_scale);
|
|
}
|
|
|
|
#[allow(clippy::single_match)] // TODO: remove when event match has multiple arms
|
|
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
|
span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
|
|
|
|
// Poll server creation
|
|
#[cfg(feature = "singleplayer")]
|
|
{
|
|
if let Some(singleplayer) = &global_state.singleplayer {
|
|
match singleplayer.receiver.try_recv() {
|
|
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)),
|
|
&mut self.client_init,
|
|
Some(runtime),
|
|
);
|
|
},
|
|
Ok(Err(e)) => {
|
|
error!(?e, "Could not start server");
|
|
global_state.singleplayer = None;
|
|
self.client_init = None;
|
|
self.main_menu_ui.cancel_connection();
|
|
self.main_menu_ui.show_info(format!("Error: {:?}", e));
|
|
},
|
|
Err(_) => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle window events.
|
|
for event in events {
|
|
// Pass all events to the ui first.
|
|
if self.main_menu_ui.handle_event(event.clone()) {
|
|
continue;
|
|
}
|
|
|
|
match event {
|
|
Event::Close => return PlayStateResult::Shutdown,
|
|
// Ignore all other events.
|
|
_ => {},
|
|
}
|
|
}
|
|
// Poll client creation.
|
|
match self.client_init.as_ref().and_then(|init| init.poll()) {
|
|
Some(InitMsg::Done(Ok(mut client))) => {
|
|
self.client_init = None;
|
|
self.main_menu_ui.connected();
|
|
// Register voxygen components / resources
|
|
crate::ecs::init(client.state_mut().ecs_mut());
|
|
return PlayStateResult::Push(Box::new(CharSelectionState::new(
|
|
global_state,
|
|
std::rc::Rc::new(std::cell::RefCell::new(client)),
|
|
)));
|
|
},
|
|
Some(InitMsg::Done(Err(err))) => {
|
|
let localized_strings = global_state.i18n.read();
|
|
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(err) => match err {
|
|
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::AlreadyLoggedIn => {
|
|
localized_strings.get("main.login.already_logged_in").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(_)),
|
|
)) => localized_strings
|
|
.get("main.login.network_wrong_version")
|
|
.into(),
|
|
client::Error::NetworkErr(e) => format!(
|
|
"{}: {:?}",
|
|
localized_strings.get("main.login.network_error"),
|
|
e
|
|
),
|
|
client::Error::ParticipantErr(e) => format!(
|
|
"{}: {:?}",
|
|
localized_strings.get("main.login.network_error"),
|
|
e
|
|
),
|
|
client::Error::StreamErr(e) => format!(
|
|
"{}: {:?}",
|
|
localized_strings.get("main.login.network_error"),
|
|
e
|
|
),
|
|
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
|
|
});
|
|
},
|
|
Some(InitMsg::IsAuthTrusted(auth_server)) => {
|
|
if global_state
|
|
.settings
|
|
.networking
|
|
.trusted_auth_servers
|
|
.contains(&auth_server)
|
|
{
|
|
// Can't fail since we just polled it, it must be Some
|
|
self.client_init
|
|
.as_ref()
|
|
.unwrap()
|
|
.auth_trust(auth_server, true);
|
|
} else {
|
|
// Show warning that auth server is not trusted and prompt for approval
|
|
self.main_menu_ui.auth_trust_prompt(auth_server);
|
|
}
|
|
},
|
|
None => {},
|
|
}
|
|
|
|
// Maintain the UI.
|
|
for event in self
|
|
.main_menu_ui
|
|
.maintain(global_state, global_state.clock.dt())
|
|
{
|
|
match event {
|
|
MainMenuEvent::LoginAttempt {
|
|
username,
|
|
password,
|
|
server_address,
|
|
} => {
|
|
let mut net_settings = &mut global_state.settings.networking;
|
|
net_settings.username = username.clone();
|
|
net_settings.default_server = server_address.clone();
|
|
if !net_settings.servers.contains(&server_address) {
|
|
net_settings.servers.push(server_address.clone());
|
|
}
|
|
global_state.settings.save_to_file_warn();
|
|
|
|
attempt_login(
|
|
&mut global_state.settings,
|
|
&mut global_state.info_message,
|
|
username,
|
|
password,
|
|
ClientConnArgs::Host(server_address),
|
|
&mut self.client_init,
|
|
None,
|
|
);
|
|
},
|
|
MainMenuEvent::CancelLoginAttempt => {
|
|
// client_init contains Some(ClientInit), which spawns a thread which contains a
|
|
// TcpStream::connect() call This call is blocking
|
|
// TODO fix when the network rework happens
|
|
#[cfg(feature = "singleplayer")]
|
|
{
|
|
global_state.singleplayer = None;
|
|
}
|
|
self.client_init = None;
|
|
self.main_menu_ui.cancel_connection();
|
|
},
|
|
MainMenuEvent::ChangeLanguage(new_language) => {
|
|
global_state.settings.language.selected_language =
|
|
new_language.language_identifier;
|
|
global_state.i18n = Localization::load_expect(&i18n_asset_key(
|
|
&global_state.settings.language.selected_language,
|
|
));
|
|
global_state.i18n.read().log_missing_entries();
|
|
self.main_menu_ui
|
|
.update_language(global_state.i18n, &global_state.settings);
|
|
},
|
|
#[cfg(feature = "singleplayer")]
|
|
MainMenuEvent::StartSingleplayer => {
|
|
let singleplayer = Singleplayer::new();
|
|
|
|
global_state.singleplayer = Some(singleplayer);
|
|
},
|
|
MainMenuEvent::Quit => return PlayStateResult::Shutdown,
|
|
// Note: Keeping in case we re-add the disclaimer
|
|
/*MainMenuEvent::DisclaimerAccepted => {
|
|
global_state.settings.show_disclaimer = false
|
|
},*/
|
|
MainMenuEvent::AuthServerTrust(auth_server, trust) => {
|
|
if trust {
|
|
global_state
|
|
.settings
|
|
.networking
|
|
.trusted_auth_servers
|
|
.insert(auth_server.clone());
|
|
global_state.settings.save_to_file_warn();
|
|
}
|
|
self.client_init
|
|
.as_ref()
|
|
.map(|init| init.auth_trust(auth_server, trust));
|
|
},
|
|
}
|
|
}
|
|
|
|
if let Some(info) = global_state.info_message.take() {
|
|
self.main_menu_ui.show_info(info);
|
|
}
|
|
|
|
PlayStateResult::Continue
|
|
}
|
|
|
|
fn name(&self) -> &'static str { "Title" }
|
|
|
|
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
|
|
// Draw the UI to the screen.
|
|
self.main_menu_ui.render(renderer);
|
|
}
|
|
}
|
|
|
|
fn attempt_login(
|
|
settings: &mut Settings,
|
|
info_message: &mut Option<String>,
|
|
username: String,
|
|
password: String,
|
|
connection_args: ClientConnArgs,
|
|
client_init: &mut Option<ClientInit>,
|
|
runtime: Option<Arc<runtime::Runtime>>,
|
|
) {
|
|
if comp::Player::alias_is_valid(&username) {
|
|
// Don't try to connect if there is already a connection in progress.
|
|
if client_init.is_none() {
|
|
*client_init = Some(ClientInit::new(
|
|
connection_args,
|
|
username,
|
|
Some(settings.graphics.view_distance),
|
|
password,
|
|
runtime,
|
|
));
|
|
}
|
|
} else {
|
|
*info_message = Some("Invalid username".to_string());
|
|
}
|
|
}
|