diff --git a/CHANGELOG.md b/CHANGELOG.md index a49bded69d..aea2312d33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Persistent waypoints (start from the last camp fire you visited) - NPCs use all three weapon skills in combat - Speed stat to weapons which affects weapon attack speed +- Saving of the last selected character in the character selection screen +- Autoselecting the newly created character +- Deselecting when the selected character is deleted ### Changed diff --git a/client/src/lib.rs b/client/src/lib.rs index 73efa29ee8..500e0f286b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -67,6 +67,7 @@ pub enum Event { Notification(Notification), SetViewDistance(u32), Outcome(Outcome), + CharacterCreated(CharacterId), } pub struct Client { @@ -1422,7 +1423,11 @@ impl Client { Ok(()) } - fn handle_server_character_screen_msg(&mut self, msg: ServerGeneral) -> Result<(), Error> { + fn handle_server_character_screen_msg( + &mut self, + events: &mut Vec, + msg: ServerGeneral, + ) -> Result<(), Error> { match msg { ServerGeneral::CharacterListUpdate(character_list) => { self.character_list.characters = character_list; @@ -1438,6 +1443,9 @@ impl Client { self.clean_state(); self.character_list.error = Some(error); }, + ServerGeneral::CharacterCreated(character_id) => { + events.push(Event::CharacterCreated(character_id)); + }, ServerGeneral::CharacterSuccess => { debug!("client is now in ingame state on server"); if let Some(vd) = self.view_distance { @@ -1490,7 +1498,7 @@ impl Client { self.handle_ping_msg(msg?)?; } if let Some(msg) = m3 { - self.handle_server_character_screen_msg(msg?)?; + self.handle_server_character_screen_msg(frontend_events, msg?)?; } if let Some(msg) = m4 { self.handle_server_in_game_msg(frontend_events, msg?)?; diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 625d07b0e1..24267ed228 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -69,6 +69,8 @@ pub enum ServerGeneral { CharacterListUpdate(Vec), /// An error occurred while creating or deleting a character CharacterActionError(String), + /// A new character was created + CharacterCreated(crate::character::CharacterId), CharacterSuccess, //Ingame related GroupUpdate(comp::group::ChangeNotification), @@ -194,7 +196,8 @@ impl ServerMsg { //Character Screen related ServerGeneral::CharacterDataLoadError(_) | ServerGeneral::CharacterListUpdate(_) - | ServerGeneral::CharacterActionError(_) => { + | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterCreated(_) => { c_type != ClientType::ChatOnly && presence.is_none() }, ServerGeneral::CharacterSuccess => { diff --git a/server/src/client.rs b/server/src/client.rs index 8c4b2655de..68a3097f5b 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -73,6 +73,7 @@ impl Client { ServerGeneral::CharacterDataLoadError(_) | ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterCreated(_) | ServerGeneral::CharacterSuccess => { self.character_screen_stream.try_lock().unwrap().send(g) }, @@ -149,6 +150,7 @@ impl Client { ServerGeneral::CharacterDataLoadError(_) | ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterCreated(_) | ServerGeneral::CharacterSuccess => { PreparedMsg::new(1, &g, &self.character_screen_stream) }, diff --git a/server/src/lib.rs b/server/src/lib.rs index 1e5d6c4b39..70a4f14e89 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -62,7 +62,7 @@ use futures_executor::block_on; use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics}; use network::{Network, Pid, ProtocolAddr}; use persistence::{ - character_loader::{CharacterLoader, CharacterLoaderResponseType}, + character_loader::{CharacterLoader, CharacterLoaderResponseKind}, character_updater::CharacterUpdater, }; use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt}; @@ -539,7 +539,7 @@ impl Server { .read_resource::() .messages() .for_each(|query_result| match query_result.result { - CharacterLoaderResponseType::CharacterList(result) => match result { + CharacterLoaderResponseKind::CharacterList(result) => match result { Ok(character_list_data) => self.notify_client( query_result.entity, ServerGeneral::CharacterListUpdate(character_list_data), @@ -549,7 +549,23 @@ impl Server { ServerGeneral::CharacterActionError(error.to_string()), ), }, - CharacterLoaderResponseType::CharacterData(result) => { + CharacterLoaderResponseKind::CharacterCreation(result) => match result { + Ok((character_id, list)) => { + self.notify_client( + query_result.entity, + ServerGeneral::CharacterListUpdate(list), + ); + self.notify_client( + query_result.entity, + ServerGeneral::CharacterCreated(character_id), + ); + }, + Err(error) => self.notify_client( + query_result.entity, + ServerGeneral::CharacterActionError(error.to_string()), + ), + }, + CharacterLoaderResponseKind::CharacterData(result) => { let message = match *result { Ok(character_data) => ServerEvent::UpdateCharacterData { entity: query_result.entity, diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index b54dbd94c1..6b9294f508 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -17,7 +17,7 @@ use crate::{ convert_stats_from_database, convert_stats_to_database, convert_waypoint_to_database_json, }, - character_loader::{CharacterDataResult, CharacterListResult}, + character_loader::{CharacterCreationResult, CharacterDataResult, CharacterListResult}, error::Error::DatabaseError, json_models::CharacterPosition, PersistedComponents, @@ -170,7 +170,7 @@ pub fn create_character( persisted_components: PersistedComponents, connection: VelorenTransaction, map: &AbilityMap, -) -> CharacterListResult { +) -> CharacterCreationResult { use schema::item::dsl::*; check_character_limit(uuid, connection)?; @@ -303,7 +303,7 @@ pub fn create_character( ))); } - load_character_list(uuid, connection, map) + load_character_list(uuid, connection, map).map(|list| (character_id, list)) } /// Delete a character. Returns the updated character list. diff --git a/server/src/persistence/character_loader.rs b/server/src/persistence/character_loader.rs index 8d72e8f80b..03982c7dde 100644 --- a/server/src/persistence/character_loader.rs +++ b/server/src/persistence/character_loader.rs @@ -12,6 +12,7 @@ use std::path::Path; use tracing::error; pub(crate) type CharacterListResult = Result, Error>; +pub(crate) type CharacterCreationResult = Result<(CharacterId, Vec), Error>; pub(crate) type CharacterDataResult = Result; type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind); @@ -38,16 +39,17 @@ enum CharacterLoaderRequestKind { /// Wrapper for results for character actions. Can be a list of /// characters, or component data belonging to an individual character #[derive(Debug)] -pub enum CharacterLoaderResponseType { +pub enum CharacterLoaderResponseKind { CharacterList(CharacterListResult), CharacterData(Box), + CharacterCreation(CharacterCreationResult), } /// Common message format dispatched in response to an update request #[derive(Debug)] pub struct CharacterLoaderResponse { pub entity: specs::Entity, - pub result: CharacterLoaderResponseType, + pub result: CharacterLoaderResponseKind, } /// A bi-directional messaging resource for making requests to modify or load @@ -80,45 +82,47 @@ impl CharacterLoader { for request in internal_rx { let (entity, kind) = request; - if let Err(e) = internal_tx.send(CharacterLoaderResponse { - entity, - result: match kind { - CharacterLoaderRequestKind::CreateCharacter { - player_uuid, - character_alias, - persisted_components, - } => CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| { - create_character( - &player_uuid, - &character_alias, + if let Err(e) = + internal_tx.send(CharacterLoaderResponse { + entity, + result: match kind { + CharacterLoaderRequestKind::CreateCharacter { + player_uuid, + character_alias, persisted_components, - txn, - &map, - ) - })), - CharacterLoaderRequestKind::DeleteCharacter { - player_uuid, - character_id, - } => CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| { - delete_character(&player_uuid, character_id, txn, &map) - })), - CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { - CharacterLoaderResponseType::CharacterList( + } => CharacterLoaderResponseKind::CharacterCreation(conn.transaction( + |txn| { + create_character( + &player_uuid, + &character_alias, + persisted_components, + txn, + &map, + ) + }, + )), + CharacterLoaderRequestKind::DeleteCharacter { + player_uuid, + character_id, + } => CharacterLoaderResponseKind::CharacterList(conn.transaction( + |txn| delete_character(&player_uuid, character_id, txn, &map), + )), + CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { + CharacterLoaderResponseKind::CharacterList(conn.transaction( + |txn| load_character_list(&player_uuid, txn, &map), + )) + }, + CharacterLoaderRequestKind::LoadCharacterData { + player_uuid, + character_id, + } => CharacterLoaderResponseKind::CharacterData(Box::new( conn.transaction(|txn| { - load_character_list(&player_uuid, txn, &map) + load_character_data(player_uuid, character_id, txn, &map) }), - ) + )), }, - CharacterLoaderRequestKind::LoadCharacterData { - player_uuid, - character_id, - } => { - CharacterLoaderResponseType::CharacterData(Box::new(conn.transaction( - |txn| load_character_data(player_uuid, character_id, txn, &map), - ))) - }, - }, - }) { + }) + { error!(?e, "Could not send send persistence request"); } } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index d9fb3b14ea..eb25e93c2e 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -30,8 +30,10 @@ impl CharSelectionState { Some("fixture.selection_bg"), &*client.borrow(), ); + let char_selection_ui = CharSelectionUi::new(global_state, &*client.borrow()); + Self { - char_selection_ui: CharSelectionUi::new(global_state), + char_selection_ui, client, scene, } @@ -98,7 +100,7 @@ impl PlayState for CharSelectionState { // Maintain the UI. let events = self .char_selection_ui - .maintain(global_state, &mut self.client.borrow_mut()); + .maintain(global_state, &self.client.borrow()); for event in events { match event { @@ -124,6 +126,15 @@ impl PlayState for CharSelectionState { ui::Event::ClearCharacterListError => { self.client.borrow_mut().character_list.error = None; }, + ui::Event::SelectCharacter(selected) => { + let client = self.client.borrow(); + let server_name = &client.server_info.name; + // Select newly created character + global_state + .profile + .set_selected_character(server_name, selected); + global_state.profile.save_to_file_warn(); + }, } } @@ -179,6 +190,9 @@ impl PlayState for CharSelectionState { ); return PlayStateResult::Pop; }, + client::Event::CharacterCreated(character_id) => { + self.char_selection_ui.select_character(character_id); + }, _ => {}, } } diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index d6358622cf..3792867664 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -130,13 +130,12 @@ pub enum Event { }, DeleteCharacter(CharacterId), ClearCharacterListError, + SelectCharacter(Option), } enum Mode { Select { info_content: Option, - // Index of selected character - selected: Option, characters_scroll: scrollable::State, character_buttons: Vec, @@ -168,7 +167,6 @@ impl Mode { pub fn select(info_content: Option) -> Self { Self::Select { info_content, - selected: None, characters_scroll: Default::default(), character_buttons: Vec::new(), new_character_button: Default::default(), @@ -231,8 +229,9 @@ struct Controls { tooltip_manager: TooltipManager, // Zone for rotating the character with the mouse mouse_detector: mouse_detector::State, - // enter: bool, mode: Mode, + // Id of the selected character + selected: Option, } #[derive(Clone)] @@ -240,7 +239,7 @@ enum Message { Back, Logout, EnterWorld, - Select(usize), + Select(CharacterId), Delete(usize), NewCharacter, CreateCharacter, @@ -265,7 +264,12 @@ enum Message { } impl Controls { - fn new(fonts: Fonts, imgs: Imgs, i18n: std::sync::Arc) -> Self { + fn new( + fonts: Fonts, + imgs: Imgs, + i18n: std::sync::Arc, + selected: Option, + ) -> Self { let version = common::util::DISPLAY_VERSION_LONG.clone(); let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str()); @@ -279,6 +283,7 @@ impl Controls { tooltip_manager: TooltipManager::new(TOOLTIP_HOVER_DUR, TOOLTIP_FADE_DUR), mouse_detector: Default::default(), mode: Mode::select(Some(InfoContent::LoadingCharacters)), + selected, } } @@ -331,7 +336,6 @@ impl Controls { let content = match &mut self.mode { Mode::Select { ref mut info_content, - selected, ref mut characters_scroll, ref mut character_buttons, ref mut new_character_button, @@ -340,6 +344,24 @@ impl Controls { ref mut yes_button, ref mut no_button, } => { + // If no character is selected then select the first one + // Note: we don't need to persist this because it is the default + if self.selected.is_none() { + self.selected = client + .character_list + .characters + .get(0) + .and_then(|i| i.character.id); + } + // Get the index of the selected character + let selected = self.selected.and_then(|id| { + client + .character_list + .characters + .iter() + .position(|i| i.character.id == Some(id)) + }); + if let Some(error) = &client.character_list.error { // TODO: use more user friendly errors with suggestions on potential solutions // instead of directly showing error message here @@ -386,75 +408,84 @@ impl Controls { let mut characters = characters .iter() .zip(character_buttons.chunks_exact_mut(2)) - .map(|(character, buttons)| { + .filter_map(|(character, buttons)| { let mut buttons = buttons.iter_mut(); - ( - character, - (buttons.next().unwrap(), buttons.next().unwrap()), - ) + // TODO: eliminate option in character id? + character.character.id.map(|id| { + ( + id, + character, + (buttons.next().unwrap(), buttons.next().unwrap()), + ) + }) }) .enumerate() - .map(|(i, (character, (select_button, delete_button)))| { - Overlay::new( - // Delete button - Button::new( - delete_button, - Space::new(Length::Units(16), Length::Units(16)), - ) - .style( - style::button::Style::new(imgs.delete_button) - .hover_image(imgs.delete_button_hover) - .press_image(imgs.delete_button_press), - ) - .on_press(Message::Delete(i)) - .with_tooltip( - tooltip_manager, - move || { - tooltip::text( - i18n.get("char_selection.delete_permanently"), - tooltip_style, - ) - }, - ), - // Select Button - AspectRatioContainer::new( + .map( + |(i, (character_id, character, (select_button, delete_button)))| { + Overlay::new( + // Delete button Button::new( - select_button, - Column::with_children(vec![ - Text::new(&character.character.alias).into(), - // TODO: only construct string once when characters are - // loaded - Text::new( - i18n.get("char_selection.level_fmt").replace( - "{level_nb}", - &character.level.to_string(), - ), - ) - .into(), - Text::new(i18n.get("char_selection.uncanny_valley")) - .into(), - ]), + delete_button, + Space::new(Length::Units(16), Length::Units(16)), ) - .padding(10) .style( - style::button::Style::new(if Some(i) == *selected { - imgs.selection_hover - } else { - imgs.selection - }) - .hover_image(imgs.selection_hover) - .press_image(imgs.selection_press), + style::button::Style::new(imgs.delete_button) + .hover_image(imgs.delete_button_hover) + .press_image(imgs.delete_button_press), ) - .width(Length::Fill) - .height(Length::Fill) - .on_press(Message::Select(i)), + .on_press(Message::Delete(i)) + .with_tooltip( + tooltip_manager, + move || { + tooltip::text( + i18n.get("char_selection.delete_permanently"), + tooltip_style, + ) + }, + ), + // Select Button + AspectRatioContainer::new( + Button::new( + select_button, + Column::with_children(vec![ + Text::new(&character.character.alias).into(), + // TODO: only construct string once when characters + // are + // loaded + Text::new( + i18n.get("char_selection.level_fmt").replace( + "{level_nb}", + &character.level.to_string(), + ), + ) + .into(), + Text::new( + i18n.get("char_selection.uncanny_valley"), + ) + .into(), + ]), + ) + .padding(10) + .style( + style::button::Style::new(if Some(i) == selected { + imgs.selection_hover + } else { + imgs.selection + }) + .hover_image(imgs.selection_hover) + .press_image(imgs.selection_press), + ) + .width(Length::Fill) + .height(Length::Fill) + .on_press(Message::Select(character_id)), + ) + .ratio_of_image(imgs.selection), ) - .ratio_of_image(imgs.selection), - ) - .padding(12) - .align_x(Align::End) - .into() - }) + .padding(12) + .align_x(Align::End) + .into() + }, + ) .collect::>(); // Add create new character button @@ -1191,20 +1222,14 @@ impl Controls { events.push(Event::Logout); }, Message::EnterWorld => { - if let Mode::Select { - selected: Some(selected), - .. - } = &self.mode - { - // TODO: eliminate option in character id? - if let Some(id) = characters.get(*selected).and_then(|i| i.character.id) { - events.push(Event::Play(id)); - } + if let (Mode::Select { .. }, Some(selected)) = (&self.mode, self.selected) { + events.push(Event::Play(selected)); } }, - Message::Select(idx) => { - if let Mode::Select { selected, .. } = &mut self.mode { - *selected = Some(idx); + Message::Select(id) => { + if let Mode::Select { .. } = &mut self.mode { + self.selected = Some(id); + events.push(Event::SelectCharacter(Some(id))) } }, Message::Delete(idx) => { @@ -1276,6 +1301,11 @@ impl Controls { if let Some(InfoContent::Deletion(idx)) = info_content { if let Some(id) = characters.get(*idx).and_then(|i| i.character.id) { events.push(Event::DeleteCharacter(id)); + // Deselect if the selected character was deleted + if Some(id) == self.selected { + self.selected = None; + events.push(Event::SelectCharacter(None)); + } } *info_content = Some(InfoContent::DeletingCharacter); } @@ -1343,8 +1373,9 @@ impl Controls { characters: &'a [CharacterItem], ) -> Option<(comp::Body, &'a comp::Loadout)> { match &self.mode { - Mode::Select { selected, .. } => selected - .and_then(|idx| characters.get(idx)) + Mode::Select { .. } => self + .selected + .and_then(|id| characters.iter().find(|i| i.character.id == Some(id))) .map(|i| (i.body, &i.loadout)), Mode::Create { loadout, body, .. } => Some((comp::Body::Humanoid(*body), loadout)), } @@ -1355,10 +1386,15 @@ pub struct CharSelectionUi { ui: Ui, controls: Controls, enter_pressed: bool, + select_character: Option, } impl CharSelectionUi { - pub fn new(global_state: &mut GlobalState) -> Self { + pub fn new(global_state: &mut GlobalState, client: &Client) -> Self { + // Load up the last selected character for this server + let server_name = &client.server_info.name; + let selected_character = global_state.profile.get_selected_character(server_name); + // Load language let i18n = Localization::load_expect(&i18n_asset_key( &global_state.settings.language.selected_language, @@ -1390,12 +1426,14 @@ impl CharSelectionUi { fonts, Imgs::load(&mut ui).expect("Failed to load images"), i18n, + selected_character, ); Self { ui, controls, enter_pressed: false, + select_character: None, } } @@ -1443,8 +1481,10 @@ impl CharSelectionUi { self.ui.set_scaling_mode(scale_mode); } + pub fn select_character(&mut self, id: CharacterId) { self.select_character = Some(id); } + // TODO: do we need whole client here or just character list? - pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec { + pub fn maintain(&mut self, global_state: &mut GlobalState, client: &Client) -> Vec { let mut events = Vec::new(); let (mut messages, _) = self.ui.maintain( @@ -1457,6 +1497,10 @@ impl CharSelectionUi { messages.push(Message::EnterWorld); } + if let Some(id) = self.select_character.take() { + messages.push(Message::Select(id)) + } + messages.into_iter().for_each(|message| { self.controls.update( message, diff --git a/voxygen/src/profile.rs b/voxygen/src/profile.rs index dbb7267b11..e1285031f9 100644 --- a/voxygen/src/profile.rs +++ b/voxygen/src/profile.rs @@ -13,21 +13,23 @@ pub struct CharacterProfile { pub hotbar_slots: [Option; 10], } +const DEFAULT_SLOTS: [Option; 10] = [ + None, + None, + None, + None, + None, + Some(hud::HotbarSlotContents::Inventory(0)), + Some(hud::HotbarSlotContents::Inventory(1)), + None, + None, + None, +]; + impl Default for CharacterProfile { fn default() -> Self { CharacterProfile { - hotbar_slots: [ - None, - None, - None, - None, - None, - Some(hud::HotbarSlotContents::Inventory(0)), - Some(hud::HotbarSlotContents::Inventory(1)), - None, - None, - None, - ], + hotbar_slots: DEFAULT_SLOTS, } } } @@ -38,12 +40,15 @@ impl Default for CharacterProfile { pub struct ServerProfile { /// A map of character's by id to their CharacterProfile. pub characters: HashMap, + // Selected character in the chararacter selection screen + pub selected_character: Option, } impl Default for ServerProfile { fn default() -> Self { ServerProfile { characters: HashMap::new(), + selected_character: None, } } } @@ -106,26 +111,23 @@ impl Profile { /// Get the hotbar_slots for the requested character_id. /// - /// if the server or character does not exist then the appropriate fields - /// will be initialised and default hotbar_slots (empty) returned. + /// If the server or character does not exist then the default hotbar_slots + /// (empty) is returned. /// /// # Arguments /// /// * server - current server the character is on. /// * character_id - id of the character. pub fn get_hotbar_slots( - &mut self, + &self, server: &str, character_id: CharacterId, ) -> [Option; 10] { self.servers - .entry(server.to_string()) - .or_insert(ServerProfile::default()) - // Get or update the CharacterProfile. - .characters - .entry(character_id) - .or_insert(CharacterProfile::default()) - .hotbar_slots + .get(server) + .and_then(|s| s.characters.get(&character_id)) + .map(|c| c.hotbar_slots) + .unwrap_or(DEFAULT_SLOTS) } /// Set the hotbar_slots for the requested character_id. @@ -154,6 +156,41 @@ impl Profile { .hotbar_slots = slots; } + /// Get the selected_character for the provided server. + /// + /// if the server does not exist then the default selected_character (None) + /// is returned. + /// + /// # Arguments + /// + /// * server - current server the character is on. + pub fn get_selected_character(&self, server: &str) -> Option { + self.servers + .get(server) + .map(|s| s.selected_character) + .unwrap_or_default() + } + + /// Set the selected_character for the provided server. + /// + /// If the server does not exist then the appropriate fields + /// will be initialised and the selected_character added. + /// + /// # Arguments + /// + /// * server - current server the character is on. + /// * selected_character - option containing selected character ID + pub fn set_selected_character( + &mut self, + server: &str, + selected_character: Option, + ) { + self.servers + .entry(server.to_string()) + .or_insert(ServerProfile::default()) + .selected_character = selected_character; + } + /// Save the current profile to disk. fn save_to_file(&self) -> std::io::Result<()> { let path = Profile::get_path(); @@ -188,7 +225,7 @@ mod tests { #[test] fn test_get_slots_with_empty_profile() { - let mut profile = Profile::default(); + let profile = Profile::default(); let slots = profile.get_hotbar_slots("TestServer", 12345); assert_eq!(slots, [ None, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index de88275739..98a3a075dd 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -178,6 +178,7 @@ impl SessionState { global_state.settings.save_to_file_warn(); }, client::Event::Outcome(outcome) => outcomes.push(outcome), + client::Event::CharacterCreated(_) => {}, } } @@ -928,7 +929,7 @@ impl PlayState for SessionState { HudEvent::ChangeHotbarState(state) => { let client = self.client.borrow(); - let server = &client.server_info.name; + let server_name = &client.server_info.name; // If we are changing the hotbar state this CANNOT be None. let character_id = match client.presence().unwrap() { PresenceKind::Character(id) => id, @@ -938,9 +939,11 @@ impl PlayState for SessionState { }; // Get or update the ServerProfile. - global_state - .profile - .set_hotbar_slots(server, character_id, state.slots); + global_state.profile.set_hotbar_slots( + server_name, + character_id, + state.slots, + ); global_state.profile.save_to_file_warn();