diff --git a/client/src/lib.rs b/client/src/lib.rs index 12e6154835..3f611de31d 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -493,13 +493,18 @@ impl Client { { const C_TYPE: ClientType = ClientType::Game; let verified = msg.verify(C_TYPE, self.registered, self.presence); - assert!( - verified, - format!( - "c_type: {:?}, registered: {}, presence: {:?}, msg: {:?}", - C_TYPE, self.registered, self.presence, msg - ) - ); + + // Due to the fact that character loading is performed asynchronously after + // initial connect it is possible to receive messages after a character load + // error while in the wrong state. + if !verified { + warn!( + "Received ClientType::Game message when not in game (Registered: {} Presence: \ + {:?}), dropping message: {:?} ", + self.registered, self.presence, msg + ); + return Ok(()); + } } match msg { ClientMsg::Type(msg) => self.register_stream.send(msg), diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index fd4e5bd0ab..0ffb0c6910 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -212,7 +212,7 @@ pub fn convert_stats_to_database( pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result { let mut inventory = Inventory::new_empty(); for db_item in database_items.iter() { - let mut item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?; + let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; // NOTE: Since this is freshly loaded, the atomic is *unique.* let comp = item.get_item_id_for_database(); @@ -269,7 +269,7 @@ pub fn convert_loadout_from_database_items( ) -> Result { let mut loadout = loadout_builder::LoadoutBuilder::new(); for db_item in database_items.iter() { - let item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?; + let item = get_item_from_asset(db_item.item_definition_id.as_str())?; // NOTE: item id is currently *unique*, so we can store the ID safely. let comp = item.get_item_id_for_database(); comp.store(Some(NonZeroU64::try_from(db_item.item_id as u64).map_err( @@ -365,3 +365,13 @@ pub fn convert_stats_from_database(stats: &Stats, alias: String) -> common::comp new_stats } + +fn get_item_from_asset(item_definition_id: &str) -> Result { + common::comp::Item::new_from_asset(item_definition_id).map_err(|err| { + Error::AssetError(format!( + "Error loading item asset: {} - {}", + item_definition_id, + err.to_string() + )) + }) +} diff --git a/server/src/persistence/character_loader.rs b/server/src/persistence/character_loader.rs index a352ae4670..c73a8425af 100644 --- a/server/src/persistence/character_loader.rs +++ b/server/src/persistence/character_loader.rs @@ -115,11 +115,19 @@ impl CharacterLoader { CharacterLoaderRequestKind::LoadCharacterData { player_uuid, character_id, - } => CharacterLoaderResponseKind::CharacterData(Box::new( - conn.transaction(|txn| { + } => { + let result = conn.transaction(|txn| { load_character_data(player_uuid, character_id, txn, &map) - }), - )), + }); + if result.is_err() { + error!( + ?result, + "Error loading character data for character_id: {}", + character_id + ); + } + CharacterLoaderResponseKind::CharacterData(Box::new(result)) + }, }, }) { diff --git a/server/src/persistence/error.rs b/server/src/persistence/error.rs index 8cffd45c8d..7d91fce01a 100644 --- a/server/src/persistence/error.rs +++ b/server/src/persistence/error.rs @@ -2,7 +2,6 @@ extern crate diesel; -use common::assets::Error as AssetsError; use std::fmt; #[derive(Debug)] @@ -40,10 +39,6 @@ impl fmt::Display for Error { } } -impl From for Error { - fn from(error: AssetsError) -> Error { Error::AssetError(error.to_string()) } -} - impl From for Error { fn from(error: diesel::result::Error) -> Error { Error::DatabaseError(error) } } diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 83c55ffc28..c79d3cd8c6 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -55,6 +55,10 @@ pub struct GlobalState { // TODO: redo this so that the watcher doesn't have to exist for reloading to occur pub i18n: AssetHandle, pub clipboard: Option, + // NOTE: This can be removed from GlobalState if client state behavior is refactored to not + // enter the game before confirmation of successful character load + /// An error returned by Client that needs to be displayed by the UI + pub client_error: Option, } impl GlobalState { diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 48d2a9a44d..16e58b0505 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -188,6 +188,7 @@ fn main() { singleplayer: None, i18n, clipboard, + client_error: None, }; run::run(global_state, event_loop); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 1356c2686c..cc836594a5 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -11,7 +11,7 @@ use crate::{ use client::{self, Client}; use common::{comp, resources::DeltaTime, span}; use specs::WorldExt; -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, mem, rc::Rc}; use tracing::error; use ui::CharSelectionUi; @@ -188,7 +188,7 @@ impl PlayState for CharSelectionState { self.char_selection_ui.select_character(character_id); }, client::Event::CharacterError(error) => { - self.char_selection_ui.display_error(error); + global_state.client_error = Some(error); }, _ => {}, } @@ -202,6 +202,10 @@ impl PlayState for CharSelectionState { }, } + if let Some(error) = mem::take(&mut global_state.client_error) { + self.char_selection_ui.display_error(error); + } + // TODO: make sure rendering is not relying on cleaned up stuff self.client.borrow_mut().cleanup(); @@ -213,7 +217,7 @@ impl PlayState for CharSelectionState { } } - fn name(&self) -> &'static str { "Title" } + fn name(&self) -> &'static str { "Character Selection" } fn render(&mut self, renderer: &mut Renderer, _: &Settings) { let client = self.client.borrow(); diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 34f711a308..a126c2e1ef 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -656,13 +656,13 @@ impl Controls { }, InfoContent::CharacterError(error) => Column::with_children(vec![ Text::new(error).size(fonts.cyri.scale(24)).into(), - Container::new(neat_button( + Row::with_children(vec![neat_button( no_button, i18n.get("common.close"), FILL_FRAC_ONE, button_style, Some(Message::ClearCharacterListError), - )) + )]) .height(Length::Units(28)) .into(), ]) @@ -679,11 +679,11 @@ impl Controls { (28, 28, 22, 255).into(), ), ) - .width(Length::Fill) - .height(Length::Fill) + .width(Length::Shrink) + .height(Length::Shrink) .max_width(400) - .max_height(130) - .padding(16) + .max_height(500) + .padding(24) .center_x() .center_y(); diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 779e26f0cc..bc9c464650 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -181,7 +181,9 @@ impl SessionState { }, client::Event::Outcome(outcome) => outcomes.push(outcome), client::Event::CharacterCreated(_) => {}, - client::Event::CharacterError(_) => {}, + client::Event::CharacterError(error) => { + global_state.client_error = Some(error); + }, } }