Wait for ingame pipelines to build before entering char select, show pipeline rebuilding status text in the graphics settings tab

This commit is contained in:
Imbris 2021-04-25 21:30:17 -04:00
parent 9c3e3ea638
commit 30f02af6a4
3 changed files with 138 additions and 28 deletions

View File

@ -35,6 +35,7 @@ widget_ids! {
window_scrollbar,
reset_graphics_button,
fps_counter,
pipeline_recreation_text,
vd_slider,
vd_text,
vd_value,
@ -210,6 +211,24 @@ impl<'a> Widget for Video<'a> {
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(18))
.set(state.ids.fps_counter, ui);
// Pipeline recreation status
if let Some((total, complete)) = self
.global_state
.window
.renderer()
.pipeline_recreation_status()
{
Text::new(&format!("Rebuilding pipelines: ({}/{})", complete, total))
.down_from(state.ids.fps_counter, 10.0)
.align_right_of(state.ids.fps_counter)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
// TODO: make color pulse or something
.color(TEXT_COLOR)
.set(state.ids.pipeline_recreation_text, ui);
}
// View Distance
Text::new(&self.localized_strings.get("hud.settings.view_distance"))
.top_left_with_margins_on(state.ids.window, 10.0, 10.0)

View File

@ -12,7 +12,7 @@ use crate::{
use client::{
addr::ConnectionArgs,
error::{InitProtocolError, NetworkConnectError, NetworkError},
ServerInfo,
Client, ServerInfo,
};
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::comp;
@ -23,10 +23,29 @@ use tokio::runtime;
use tracing::error;
use ui::{Event as MainMenuEvent, MainMenuUi};
// TODO: show status messages for waiting on server creation, client init, and
// pipeline creation (we can show progress of pipeline creation)
enum InitState {
None,
// Waiting on the client initialization
Client(ClientInit),
// Client initialized but still waiting on Renderer pipeline creation
Pipeline(Client),
}
impl InitState {
fn client(&self) -> Option<&ClientInit> {
if let Self::Client(client_init) = &self {
Some(client_init)
} else {
None
}
}
}
pub struct MainMenuState {
main_menu_ui: MainMenuUi,
// Used for client creation.
client_init: Option<ClientInit>,
init: InitState,
scene: Scene,
}
@ -35,7 +54,7 @@ impl MainMenuState {
pub fn new(global_state: &mut GlobalState) -> Self {
Self {
main_menu_ui: MainMenuUi::new(global_state),
client_init: None,
init: InitState::None,
scene: Scene::new(global_state.window.renderer_mut()),
}
}
@ -78,14 +97,14 @@ impl PlayState for MainMenuState {
"singleplayer".to_owned(),
"".to_owned(),
ConnectionArgs::Mpsc(14004),
&mut self.client_init,
&mut self.init,
Some(runtime),
);
},
Ok(Err(e)) => {
error!(?e, "Could not start server");
global_state.singleplayer = None;
self.client_init = None;
self.init = InitState::None;
self.main_menu_ui.cancel_connection();
self.main_menu_ui.show_info(format!("Error: {:?}", e));
},
@ -108,19 +127,14 @@ impl PlayState for MainMenuState {
}
}
// Poll client creation.
match self.client_init.as_ref().and_then(|init| init.poll()) {
match self.init.client().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)),
)));
self.init = InitState::Pipeline(client);
},
Some(InitMsg::Done(Err(e))) => {
self.client_init = None;
self.init = InitState::None;
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
@ -136,10 +150,7 @@ impl PlayState for MainMenuState {
.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);
self.init.client().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);
@ -148,6 +159,64 @@ impl PlayState for MainMenuState {
None => {},
}
// Tick the client to keep the connection alive if we are waiting on pipelines
let localized_strings = &global_state.i18n.read();
if let InitState::Pipeline(client) = &mut self.init {
match client.tick(
comp::ControllerInputs::default(),
global_state.clock.dt(),
|_| {},
) {
Ok(events) => {
for event in events {
match event {
client::Event::SetViewDistance(vd) => {
global_state.settings.graphics.view_distance = vd;
global_state.settings.save_to_file_warn();
},
client::Event::Disconnect => {
global_state.info_message = Some(
localized_strings
.get("main.login.server_shut_down")
.to_owned(),
);
self.init = InitState::None;
},
_ => {},
}
}
},
Err(err) => {
global_state.info_message =
Some(localized_strings.get("common.connection_lost").to_owned());
error!(?err, "[main menu] Failed to tick the client");
self.init = InitState::None;
},
}
}
// Poll renderer pipeline creation
if let InitState::Pipeline(..) = &self.init {
// If not complete go to char select screen
if global_state
.window
.renderer()
.pipeline_creation_status()
.is_none()
{
// Always succeeds since we check above
if let InitState::Pipeline(client) =
core::mem::replace(&mut self.init, InitState::None)
{
self.main_menu_ui.connected();
return PlayStateResult::Push(Box::new(CharSelectionState::new(
global_state,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)));
}
}
}
// Maintain the UI.
for event in self
.main_menu_ui
@ -184,19 +253,19 @@ impl PlayState for MainMenuState {
username,
password,
connection_args,
&mut self.client_init,
&mut self.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
// init contains InitState::Client(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.init = InitState::None;
self.main_menu_ui.cancel_connection();
},
MainMenuEvent::ChangeLanguage(new_language) => {
@ -232,8 +301,8 @@ impl PlayState for MainMenuState {
.insert(auth_server.clone());
global_state.settings.save_to_file_warn();
}
self.client_init
.as_ref()
self.init
.client()
.map(|init| init.auth_trust(auth_server, trust));
},
}
@ -369,7 +438,7 @@ fn attempt_login(
username: String,
password: String,
connection_args: ConnectionArgs,
client_init: &mut Option<ClientInit>,
init: &mut InitState,
runtime: Option<Arc<runtime::Runtime>>,
) {
if let Err(err) = comp::Player::alias_validate(&username) {
@ -378,8 +447,8 @@ fn attempt_login(
}
// Don't try to connect if there is already a connection in progress.
if client_init.is_none() {
*client_init = Some(ClientInit::new(
if let InitState::None = init {
*init = InitState::Client(ClientInit::new(
connection_args,
username,
password,

View File

@ -388,6 +388,28 @@ impl Renderer {
})
}
/// Check the status of the intial pipeline creation
/// Returns `None` if complete
/// Returns `Some((total, complete))` if in progress
pub fn pipeline_creation_status(&self) -> Option<(usize, usize)> {
if let State::Interface { creating, .. } = &self.state {
Some(creating.status())
} else {
None
}
}
/// Check the status the pipeline recreation
/// Returns `None` if pipelines are currently not being recreated
/// Returns `Some((total, complete))` if in progress
pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> {
if let State::Complete { recreating, .. } = &self.state {
recreating.as_ref().map(|r| r.status())
} else {
None
}
}
/// Change the render mode.
pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> {
// TODO: are there actually any issues with the current mode not matching the