From fb8590fac78c9e773e6b945dd1354a37b1eee7e4 Mon Sep 17 00:00:00 2001 From: Shane Handley Date: Sun, 5 Jul 2020 06:53:15 +1000 Subject: [PATCH] Load and insert a characters achievement list after completion of their body/stats/inventory data. --- client/src/lib.rs | 13 ++++++------- common/src/comp/achievement.rs | 12 ++++++++++-- common/src/event.rs | 2 +- common/src/msg/server.rs | 6 +++--- server/data/achievements.ron | 9 ++++++--- server/src/events/entity_creation.rs | 2 +- server/src/lib.rs | 12 ++++++++---- server/src/persistence/achievement.rs | 14 ++++++++------ server/src/persistence/character.rs | 3 ++- server/src/state_ext.rs | 16 +++++++++++++--- server/src/sys/achievement.rs | 2 +- 11 files changed, 59 insertions(+), 32 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index de2c2a66a7..a45834305f 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -992,16 +992,15 @@ impl Client { self.view_distance = Some(vd); frontend_events.push(Event::SetViewDistance(vd)); }, - ServerMsg::AchievementDataUpdate(achievements) => { - // TODO should this happen? Can't you just save it - // against the entity on the server and it will et - // synced? + ServerMsg::CharacterAchievementDataLoaded(achievement_list) => { + self.state.write_component(self.entity, achievement_list); }, - ServerMsg::AchievementDataError(error) => { + ServerMsg::CharacterAchievementDataError(error) => { // TODO handle somehow }, - ServerMsg::AchievementCompletion => { - tracing::info!("Completed achievement"); + ServerMsg::AchievementCompletion(achievement) => { + // TODO handle in UI + tracing::info!(?achievement, "Completed achievement"); }, } } diff --git a/common/src/comp/achievement.rs b/common/src/comp/achievement.rs index 2cb5bd2fa1..4409cf05a5 100644 --- a/common/src/comp/achievement.rs +++ b/common/src/comp/achievement.rs @@ -88,8 +88,12 @@ impl Achievement { } } -/// The achievement List assigned to all players. This holds a list of -/// achievements where the player has made some progress towards completion. +/// Each character is assigned an achievement list, which holds information +/// about which achievements that the player has made some progress on, or +/// completed. +/// +/// This minimises storage of data per-character, and can be merged with a full +/// achievement list #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct AchievementList(Vec); @@ -97,6 +101,10 @@ impl Default for AchievementList { fn default() -> AchievementList { AchievementList(Vec::new()) } } +impl AchievementList { + pub fn from(data: Vec) -> Self { Self(data) } +} + impl Component for AchievementList { type Storage = FlaggedStorage>; } diff --git a/common/src/event.rs b/common/src/event.rs index 7bcb6683db..70a6ebfbda 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -60,7 +60,7 @@ pub enum ServerEvent { }, UpdateCharacterData { entity: EcsEntity, - components: (comp::Body, comp::Stats, comp::Inventory, comp::Loadout), + components: (i32, comp::Body, comp::Stats, comp::Inventory, comp::Loadout), }, ExitIngame { entity: EcsEntity, diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 99c5c231d7..d7922d5bba 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -61,11 +61,11 @@ pub enum ServerMsg { }, /// A list of achievements which the character has fully or partially /// completed - AchievementDataUpdate(Vec), + CharacterAchievementDataLoaded(comp::AchievementList), /// An error occurred while loading character achievements - AchievementDataError(String), + CharacterAchievementDataError(String), /// The client has completed an achievement - AchievementCompletion, + AchievementCompletion(comp::AchievementItem), /// An error occurred while loading character data CharacterDataLoadError(String), /// A list of characters belonging to the a authenticated player was sent diff --git a/server/data/achievements.ron b/server/data/achievements.ron index aa5798ba67..b7ad1a3fbe 100644 --- a/server/data/achievements.ron +++ b/server/data/achievements.ron @@ -1,9 +1,12 @@ [ ( title: "Collect 5 Apples", - action: CollectItemKind(Consumable( - kind: Apple - )), + action: CollectConsumable(Apple), target: 5 + ), + ( + title: "Collect 7 Apples", + action: CollectConsumable(Apple), + target: 7 ) ] \ No newline at end of file diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index fb8606e37a..f01192a98c 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -16,7 +16,7 @@ pub fn handle_initialize_character(server: &mut Server, entity: EcsEntity, chara pub fn handle_loaded_character_data( server: &mut Server, entity: EcsEntity, - loaded_components: (comp::Body, comp::Stats, comp::Inventory, comp::Loadout), + loaded_components: (i32, comp::Body, comp::Stats, comp::Inventory, comp::Loadout), ) { server .state diff --git a/server/src/lib.rs b/server/src/lib.rs index e9cc228fc1..184456dd9f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -486,10 +486,14 @@ impl Server { entity, result, )) => match result { - Ok(achievement_data) => self - .notify_client(entity, ServerMsg::AchievementDataUpdate(achievement_data)), - Err(error) => self - .notify_client(entity, ServerMsg::AchievementDataError(error.to_string())), + Ok(achievement_data) => self.notify_client( + entity, + ServerMsg::CharacterAchievementDataLoaded(achievement_data), + ), + Err(error) => self.notify_client( + entity, + ServerMsg::CharacterAchievementDataError(error.to_string()), + ), }, }); diff --git a/server/src/persistence/achievement.rs b/server/src/persistence/achievement.rs index ec903ed274..3277455cad 100644 --- a/server/src/persistence/achievement.rs +++ b/server/src/persistence/achievement.rs @@ -29,7 +29,7 @@ enum AchievementLoaderRequestKind { }, } -type LoadCharacterAchievementsResult = (specs::Entity, Result, Error>); +type LoadCharacterAchievementsResult = (specs::Entity, Result); /// Wrapper for results #[derive(Debug)] @@ -100,15 +100,17 @@ impl Drop for AchievementLoader { fn load_character_achievement_list( character_id: i32, db_dir: &str, -) -> Result, Error> { +) -> Result { let character_achievements = schema::character_achievement::dsl::character_achievement .filter(schema::character_achievement::character_id.eq(character_id)) .load::(&establish_connection(db_dir))?; - Ok(character_achievements - .iter() - .map(comp::Achievement::from) - .collect::>()) + Ok(comp::AchievementList::from( + character_achievements + .iter() + .map(comp::Achievement::from) + .collect::>(), + )) } pub fn sync(db_dir: &str) -> Result, Error> { diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 1fe6f8f657..f1a7c65f08 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -49,7 +49,7 @@ enum CharacterLoaderRequestKind { } /// A tuple of the components that are persisted to the DB for each character -pub type PersistedComponents = (comp::Body, comp::Stats, comp::Inventory, comp::Loadout); +pub type PersistedComponents = (i32, comp::Body, comp::Stats, comp::Inventory, comp::Loadout); type CharacterListResult = Result, Error>; type CharacterDataResult = Result; @@ -236,6 +236,7 @@ impl Drop for CharacterLoader { fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> CharacterDataResult { let connection = establish_connection(db_dir); +<<<<<<< HEAD let result = schema::character::dsl::character .filter(schema::character::id.eq(character_id)) .filter(schema::character::player_uuid.eq(player_uuid)) diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 164634e60e..3cb737d759 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,6 +1,9 @@ use crate::{ - client::Client, persistence::character::PersistedComponents, settings::ServerSettings, - sys::sentinel::DeletedEntities, SpawnPoint, + client::Client, + persistence::{achievement::AchievementLoader, character::PersistedComponents}, + settings::ServerSettings, + sys::sentinel::DeletedEntities, + SpawnPoint, }; use common::{ comp, @@ -208,7 +211,14 @@ impl StateExt for State { } fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { - let (body, stats, inventory, loadout) = components; + let (character_id, body, stats, inventory, loadout) = components; + + // Now that data essential for loading into the world has returned, kick of a + // request for supplemental game data, such as achievements + self.ecs() + .read_resource::() + .load_character_achievement_list(entity, character_id); + // Make sure physics are accepted. self.write_component(entity, comp::ForceUpdate); diff --git a/server/src/sys/achievement.rs b/server/src/sys/achievement.rs index 622b608a3c..284de8d267 100644 --- a/server/src/sys/achievement.rs +++ b/server/src/sys/achievement.rs @@ -37,7 +37,7 @@ impl<'a> System<'a> for Sys { .process_achievement(Achievement::from(achievement), ach_update.event()) == true { - client.notify(ServerMsg::AchievementCompletion); + client.notify(ServerMsg::AchievementCompletion(achievement_item)); } } });