mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Replace the Achievement system with just plain events which are processed as part of the server tick. Instead of adding Achievement updates to a component belonging to the entity, use an event queue - this allows an entity to have multiple achievement checks in a single tick.
This commit is contained in:
parent
e607ec498d
commit
efe7a62c92
@ -997,6 +997,7 @@ impl Client {
|
|||||||
},
|
},
|
||||||
ServerMsg::CharacterAchievementDataError(error) => {
|
ServerMsg::CharacterAchievementDataError(error) => {
|
||||||
// TODO handle somehow
|
// TODO handle somehow
|
||||||
|
tracing::info!(?error, "Failed to load achievements");
|
||||||
},
|
},
|
||||||
ServerMsg::AchievementCompletion(achievement) => {
|
ServerMsg::AchievementCompletion(achievement) => {
|
||||||
// TODO handle in UI
|
// TODO handle in UI
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
use crate::comp::{
|
use crate::comp::item::{Consumable, Item, ItemKind};
|
||||||
self,
|
use specs::{Component, Entity, FlaggedStorage};
|
||||||
item::{Consumable, Item, ItemKind},
|
|
||||||
};
|
|
||||||
use specs::{Component, FlaggedStorage};
|
|
||||||
use specs_idvs::IDVStorage;
|
use specs_idvs::IDVStorage;
|
||||||
|
|
||||||
|
/// Used for in-game events that contribute towards player achievements.
|
||||||
|
///
|
||||||
|
/// For example, when an `InventoryManip` is detected, we record that event in
|
||||||
|
/// order to process achievements which depend on collecting items.
|
||||||
|
pub struct AchievementTrigger {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub event: AchievementEvent,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum AchievementEvent {
|
pub enum AchievementEvent {
|
||||||
None,
|
None,
|
||||||
@ -35,7 +41,7 @@ pub struct AchievementItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AchievementItem {
|
impl AchievementItem {
|
||||||
pub fn matches_event(&self, event: AchievementEvent) -> bool {
|
pub fn matches_event(&self, event: &AchievementEvent) -> bool {
|
||||||
match event {
|
match event {
|
||||||
AchievementEvent::KilledNpc => self.action == AchievementAction::KillNpcs,
|
AchievementEvent::KilledNpc => self.action == AchievementAction::KillNpcs,
|
||||||
AchievementEvent::KilledPlayer => self.action == AchievementAction::KillPlayers,
|
AchievementEvent::KilledPlayer => self.action == AchievementAction::KillPlayers,
|
||||||
@ -51,14 +57,6 @@ impl AchievementItem {
|
|||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
AchievementEvent::None => false,
|
AchievementEvent::None => false,
|
||||||
_ => {
|
|
||||||
tracing::warn!(
|
|
||||||
?event,
|
|
||||||
"An AchievementEvent was processed but the event was not handled"
|
|
||||||
);
|
|
||||||
|
|
||||||
false
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,10 +77,10 @@ impl Achievement {
|
|||||||
/// incremented by 1. This covers many cases, but using this method allows
|
/// incremented by 1. This covers many cases, but using this method allows
|
||||||
/// handling of unique types of achievements which are not simple
|
/// handling of unique types of achievements which are not simple
|
||||||
/// counters for events
|
/// counters for events
|
||||||
pub fn increment_progress(&mut self, event: AchievementEvent) -> bool {
|
pub fn increment_progress(&mut self, event: &AchievementEvent) -> bool {
|
||||||
match event {
|
match event {
|
||||||
AchievementEvent::LevelUp(level) => {
|
AchievementEvent::LevelUp(level) => {
|
||||||
self.progress = level as usize;
|
self.progress = *level as usize;
|
||||||
},
|
},
|
||||||
_ => self.progress += 1,
|
_ => self.progress += 1,
|
||||||
};
|
};
|
||||||
@ -123,7 +121,7 @@ impl AchievementList {
|
|||||||
pub fn process_achievement(
|
pub fn process_achievement(
|
||||||
&mut self,
|
&mut self,
|
||||||
achievement: Achievement,
|
achievement: Achievement,
|
||||||
event: AchievementEvent,
|
event: &AchievementEvent,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let id = achievement.id;
|
let id = achievement.id;
|
||||||
|
|
||||||
@ -131,7 +129,7 @@ impl AchievementList {
|
|||||||
self.0.push(achievement);
|
self.0.push(achievement);
|
||||||
}
|
}
|
||||||
|
|
||||||
return if let Some(char_achievement) = self.item_by_id(id) {
|
if let Some(char_achievement) = self.item_by_id(id) {
|
||||||
if char_achievement.completed {
|
if char_achievement.completed {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -141,30 +139,10 @@ impl AchievementList {
|
|||||||
tracing::warn!("Failed to find achievement after inserting");
|
tracing::warn!("Failed to find achievement after inserting");
|
||||||
|
|
||||||
false
|
false
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used as a container for in-game events that contribute towards player
|
|
||||||
/// achievements.
|
|
||||||
///
|
|
||||||
/// For example, when an `InventoryManip` is detected, we record that event in
|
|
||||||
/// order to process achievements which depend on collecting items.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct AchievementUpdate {
|
|
||||||
event: AchievementEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AchievementUpdate {
|
|
||||||
pub fn new(event: AchievementEvent) -> Self { Self { event } }
|
|
||||||
|
|
||||||
pub fn event(&self) -> AchievementEvent { self.event.clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for AchievementUpdate {
|
|
||||||
type Storage = IDVStorage<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -181,7 +159,7 @@ mod tests {
|
|||||||
let event =
|
let event =
|
||||||
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.apple"));
|
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.apple"));
|
||||||
|
|
||||||
assert!(item.matches_event(event));
|
assert!(item.matches_event(&event));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -195,7 +173,7 @@ mod tests {
|
|||||||
let event =
|
let event =
|
||||||
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.apple"));
|
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.apple"));
|
||||||
|
|
||||||
assert_eq!(item.matches_event(event), false);
|
assert_eq!(item.matches_event(&event), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -208,7 +186,7 @@ mod tests {
|
|||||||
|
|
||||||
let event = AchievementEvent::LevelUp(3);
|
let event = AchievementEvent::LevelUp(3);
|
||||||
|
|
||||||
assert_eq!(item.matches_event(event), true);
|
assert_eq!(item.matches_event(&event), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -233,12 +211,12 @@ mod tests {
|
|||||||
|
|
||||||
// The first two increments should not indicate that it is complete
|
// The first two increments should not indicate that it is complete
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement.clone(), event.clone()),
|
achievement_list.process_achievement(achievement.clone(), &event),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement.clone(), event.clone()),
|
achievement_list.process_achievement(achievement.clone(), &event),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -246,7 +224,7 @@ mod tests {
|
|||||||
|
|
||||||
// It should return true when completed
|
// It should return true when completed
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement, event),
|
achievement_list.process_achievement(achievement, &event),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -274,7 +252,8 @@ mod tests {
|
|||||||
let mut achievement_list = AchievementList::default();
|
let mut achievement_list = AchievementList::default();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement.clone(), AchievementEvent::LevelUp(6)),
|
achievement_list
|
||||||
|
.process_achievement(achievement.clone(), &AchievementEvent::LevelUp(6)),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -283,7 +262,7 @@ mod tests {
|
|||||||
assert_eq!(achievement_list.0.get(0).unwrap().completed, false);
|
assert_eq!(achievement_list.0.get(0).unwrap().completed, false);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement, AchievementEvent::LevelUp(10)),
|
achievement_list.process_achievement(achievement, &AchievementEvent::LevelUp(10)),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -313,7 +292,7 @@ mod tests {
|
|||||||
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.mushroom"));
|
AchievementEvent::CollectedItem(assets::load_expect_cloned("common.items.mushroom"));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
achievement_list.process_achievement(achievement, event),
|
achievement_list.process_achievement(achievement, &event),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ mod visual;
|
|||||||
pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout};
|
pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout};
|
||||||
pub use achievement::{
|
pub use achievement::{
|
||||||
Achievement, AchievementAction, AchievementEvent, AchievementItem, AchievementList,
|
Achievement, AchievementAction, AchievementEvent, AchievementItem, AchievementList,
|
||||||
AchievementUpdate,
|
AchievementTrigger,
|
||||||
};
|
};
|
||||||
pub use admin::{Admin, AdminList};
|
pub use admin::{Admin, AdminList};
|
||||||
pub use agent::{Agent, Alignment};
|
pub use agent::{Agent, Alignment};
|
||||||
|
@ -150,7 +150,6 @@ impl State {
|
|||||||
ecs.register::<comp::WaypointArea>();
|
ecs.register::<comp::WaypointArea>();
|
||||||
ecs.register::<comp::ForceUpdate>();
|
ecs.register::<comp::ForceUpdate>();
|
||||||
ecs.register::<comp::InventoryUpdate>();
|
ecs.register::<comp::InventoryUpdate>();
|
||||||
ecs.register::<comp::AchievementUpdate>();
|
|
||||||
ecs.register::<comp::Admin>();
|
ecs.register::<comp::Admin>();
|
||||||
ecs.register::<comp::Waypoint>();
|
ecs.register::<comp::Waypoint>();
|
||||||
ecs.register::<comp::Projectile>();
|
ecs.register::<comp::Projectile>();
|
||||||
|
@ -2,9 +2,10 @@ use crate::{client::Client, Server, SpawnPoint, StateExt};
|
|||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
comp::{
|
comp::{
|
||||||
self, item::lottery::Lottery, object, AchievementEvent, Alignment, Body, HealthChange,
|
self, item::lottery::Lottery, object, AchievementEvent, AchievementTrigger, Alignment,
|
||||||
HealthSource, Player, Stats,
|
Body, HealthChange, HealthSource, Player, Stats,
|
||||||
},
|
},
|
||||||
|
event::EventBus,
|
||||||
msg::{PlayerListUpdate, ServerMsg},
|
msg::{PlayerListUpdate, ServerMsg},
|
||||||
state::BlockChange,
|
state::BlockChange,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
@ -90,10 +91,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
let _ = state
|
state
|
||||||
.ecs()
|
.ecs()
|
||||||
.write_storage()
|
.read_resource::<EventBus<AchievementTrigger>>()
|
||||||
.insert(entity, comp::AchievementUpdate::new(event));
|
.emit_now(AchievementTrigger { entity, event });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -339,11 +340,15 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.expect("Failed to fetch uid component for entity.");
|
.expect("Failed to fetch uid component for entity.");
|
||||||
|
|
||||||
// Write an achievement update to trigger level achievements
|
// Emit an achievement check for the level up
|
||||||
let _ = server.state.ecs().write_storage().insert(
|
server
|
||||||
entity,
|
.state
|
||||||
comp::AchievementUpdate::new(AchievementEvent::LevelUp(new_level)),
|
.ecs()
|
||||||
);
|
.read_resource::<EventBus<AchievementTrigger>>()
|
||||||
|
.emit_now(AchievementTrigger {
|
||||||
|
entity,
|
||||||
|
event: AchievementEvent::LevelUp(new_level),
|
||||||
|
});
|
||||||
|
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
|
@ -3,9 +3,9 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self, item,
|
self, item,
|
||||||
slot::{self, Slot},
|
slot::{self, Slot},
|
||||||
AchievementEvent, Pos, MAX_PICKUP_RANGE_SQR,
|
AchievementEvent, AchievementTrigger, Pos, MAX_PICKUP_RANGE_SQR,
|
||||||
},
|
},
|
||||||
event::AchievementEvent,
|
event::{AchievementEvent, EventBus},
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
terrain::block::Block,
|
terrain::block::Block,
|
||||||
vol::{ReadVol, Vox},
|
vol::{ReadVol, Vox},
|
||||||
@ -86,10 +86,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
|
|
||||||
let item = picked_up_item.unwrap();
|
let item = picked_up_item.unwrap();
|
||||||
|
|
||||||
state.write_component(
|
state
|
||||||
entity,
|
.ecs()
|
||||||
comp::AchievementUpdate::new(AchievementEvent::CollectedItem(item.clone())),
|
.read_resource::<EventBus<AchievementTrigger>>()
|
||||||
);
|
.emit_now(AchievementTrigger {
|
||||||
|
entity,
|
||||||
|
event: AchievementEvent::CollectedItem(item.clone()),
|
||||||
|
});
|
||||||
|
|
||||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected(item))
|
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected(item))
|
||||||
} else {
|
} else {
|
||||||
@ -119,12 +122,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
&& state.try_set_block(pos, Block::empty()).is_some()
|
&& state.try_set_block(pos, Block::empty()).is_some()
|
||||||
{
|
{
|
||||||
comp::Item::try_reclaim_from_block(block).map(|item| {
|
comp::Item::try_reclaim_from_block(block).map(|item| {
|
||||||
state.write_component(
|
state
|
||||||
entity,
|
.ecs()
|
||||||
comp::AchievementUpdate::new(AchievementEvent::CollectedItem(
|
.read_resource::<EventBus<AchievementTrigger>>()
|
||||||
item.clone(),
|
.emit_now(AchievementTrigger {
|
||||||
)),
|
entity,
|
||||||
);
|
event: AchievementEvent::CollectedItem(item.clone()),
|
||||||
|
});
|
||||||
|
|
||||||
state.give_item(entity, item);
|
state.give_item(entity, item);
|
||||||
});
|
});
|
||||||
|
@ -125,4 +125,8 @@ impl Server {
|
|||||||
|
|
||||||
frontend_events
|
frontend_events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub fn handle_achievement_events(&mut self) -> {
|
||||||
|
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ use futures_util::{select, FutureExt};
|
|||||||
use metrics::{ServerMetrics, TickMetrics};
|
use metrics::{ServerMetrics, TickMetrics};
|
||||||
use network::{Address, Network, Pid};
|
use network::{Address, Network, Pid};
|
||||||
use persistence::{
|
use persistence::{
|
||||||
achievement::{AchievementLoader, AchievementLoaderResponse, AvailableAchievements},
|
achievement::{Achievement, AchievementLoader, AchievementLoaderResponse},
|
||||||
character::{CharacterLoader, CharacterLoaderResponseType, CharacterUpdater},
|
character::{CharacterLoader, CharacterLoaderResponseType, CharacterUpdater},
|
||||||
};
|
};
|
||||||
use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt};
|
use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt};
|
||||||
@ -89,6 +89,8 @@ pub struct Server {
|
|||||||
|
|
||||||
metrics: ServerMetrics,
|
metrics: ServerMetrics,
|
||||||
tick_metrics: TickMetrics,
|
tick_metrics: TickMetrics,
|
||||||
|
|
||||||
|
achievement_data: Vec<Achievement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
@ -101,6 +103,9 @@ impl Server {
|
|||||||
|
|
||||||
// Event Emitters
|
// Event Emitters
|
||||||
state.ecs_mut().insert(EventBus::<ServerEvent>::default());
|
state.ecs_mut().insert(EventBus::<ServerEvent>::default());
|
||||||
|
state
|
||||||
|
.ecs_mut()
|
||||||
|
.insert(EventBus::<comp::AchievementTrigger>::default());
|
||||||
|
|
||||||
state.ecs_mut().insert(AuthProvider::new(
|
state.ecs_mut().insert(AuthProvider::new(
|
||||||
settings.auth_server_address.clone(),
|
settings.auth_server_address.clone(),
|
||||||
@ -261,13 +266,14 @@ impl Server {
|
|||||||
|
|
||||||
// TODO I switched this to return comp::Achievement but that's not right...we
|
// TODO I switched this to return comp::Achievement but that's not right...we
|
||||||
// want the id really,
|
// want the id really,
|
||||||
match persistence::achievement::sync(&settings.persistence_db_dir) {
|
let achievement_data = match persistence::achievement::sync(&settings.persistence_db_dir) {
|
||||||
Ok(achievements) => {
|
Ok(achievements) => achievements,
|
||||||
info!("Achievement data loaded...");
|
Err(e) => {
|
||||||
state.ecs_mut().insert(AvailableAchievements(achievements));
|
error!(?e, "Achievement data migration error");
|
||||||
|
|
||||||
|
Vec::new()
|
||||||
},
|
},
|
||||||
Err(e) => error!(?e, "Achievement data migration error"),
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
state,
|
state,
|
||||||
@ -280,6 +286,8 @@ impl Server {
|
|||||||
|
|
||||||
metrics,
|
metrics,
|
||||||
tick_metrics,
|
tick_metrics,
|
||||||
|
|
||||||
|
achievement_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(?settings, "created veloren server with");
|
debug!(?settings, "created veloren server with");
|
||||||
@ -429,6 +437,43 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Achievement processing
|
||||||
|
let achievement_events = self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.read_resource::<EventBus<comp::AchievementTrigger>>()
|
||||||
|
.recv_all();
|
||||||
|
|
||||||
|
for trigger in achievement_events {
|
||||||
|
// Get the achievement that matches this event
|
||||||
|
for achievement in &self.achievement_data {
|
||||||
|
let achievement_item = comp::AchievementItem::from(&achievement.details);
|
||||||
|
|
||||||
|
if achievement_item.matches_event(&trigger.event) {
|
||||||
|
// Calls to `process_achievement` return true to indicate that the
|
||||||
|
// achievement is complete. In this case, we notify the client to notify them of
|
||||||
|
// completing the achievement
|
||||||
|
if let Some(achievement_list) = self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.write_storage::<comp::AchievementList>()
|
||||||
|
.get_mut(trigger.entity)
|
||||||
|
{
|
||||||
|
if achievement_list.process_achievement(
|
||||||
|
comp::Achievement::from(achievement),
|
||||||
|
&trigger.event,
|
||||||
|
) == true
|
||||||
|
{
|
||||||
|
self.notify_client(
|
||||||
|
trigger.entity,
|
||||||
|
ServerMsg::AchievementCompletion(achievement_item),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 7 Persistence updates
|
// 7 Persistence updates
|
||||||
let before_persistence_updates = Instant::now();
|
let before_persistence_updates = Instant::now();
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
|
pub type Achievement = AchievementModel;
|
||||||
|
|
||||||
/// Available database operations when modifying a player's characetr list
|
/// Available database operations when modifying a player's characetr list
|
||||||
enum AchievementLoaderRequestKind {
|
enum AchievementLoaderRequestKind {
|
||||||
LoadCharacterAchievementList {
|
LoadCharacterAchievementList {
|
||||||
@ -237,10 +239,3 @@ pub fn hash<T: Hash>(t: &T) -> u64 {
|
|||||||
t.hash(&mut s);
|
t.hash(&mut s);
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds a list of achievements available to players.
|
|
||||||
///
|
|
||||||
/// This acts as the reference for checks on achievements, and holds id's as
|
|
||||||
/// well as details of achievement
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AvailableAchievements(pub Vec<AchievementModel>);
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
use crate::persistence::achievement::AvailableAchievements;
|
|
||||||
|
|
||||||
use crate::client::Client;
|
|
||||||
use common::{
|
|
||||||
comp::{Achievement, AchievementItem, AchievementList, AchievementUpdate},
|
|
||||||
msg::ServerMsg,
|
|
||||||
};
|
|
||||||
use specs::{Join, ReadExpect, System, WriteStorage};
|
|
||||||
pub struct Sys;
|
|
||||||
|
|
||||||
impl<'a> System<'a> for Sys {
|
|
||||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
|
||||||
type SystemData = (
|
|
||||||
WriteStorage<'a, Client>,
|
|
||||||
WriteStorage<'a, AchievementList>,
|
|
||||||
WriteStorage<'a, AchievementUpdate>,
|
|
||||||
ReadExpect<'a, AvailableAchievements>,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&mut self,
|
|
||||||
(mut clients, mut achievement_lists, mut achievement_updates, available_achievements): Self::SystemData,
|
|
||||||
) {
|
|
||||||
// TODO filter out achievements which do not care about this event here, then
|
|
||||||
// iterate over them
|
|
||||||
for (client, achievement_list, ach_update) in
|
|
||||||
(&mut clients, &mut achievement_lists, &achievement_updates).join()
|
|
||||||
{
|
|
||||||
(available_achievements.0).iter().for_each(|achievement| {
|
|
||||||
let achievement_item = AchievementItem::from(&achievement.details);
|
|
||||||
|
|
||||||
if achievement_item.matches_event(ach_update.event()) {
|
|
||||||
// Calls to `process_achievement` return true to indicate that the
|
|
||||||
// achievement is complete. In this case, we notify the client to notify them of
|
|
||||||
// completing the achievement
|
|
||||||
if achievement_list
|
|
||||||
.process_achievement(Achievement::from(achievement), ach_update.event())
|
|
||||||
== true
|
|
||||||
{
|
|
||||||
client.notify(ServerMsg::AchievementCompletion(achievement_item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
achievement_updates.clear();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,3 @@
|
|||||||
pub mod achievement;
|
|
||||||
pub mod entity_sync;
|
pub mod entity_sync;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
@ -54,9 +53,6 @@ pub fn run_sync_systems(ecs: &mut specs::World) {
|
|||||||
// Sync
|
// Sync
|
||||||
terrain_sync::Sys.run_now(ecs);
|
terrain_sync::Sys.run_now(ecs);
|
||||||
entity_sync::Sys.run_now(ecs);
|
entity_sync::Sys.run_now(ecs);
|
||||||
|
|
||||||
// Test
|
|
||||||
achievement::Sys.run_now(ecs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to schedule systems to run at an interval
|
/// Used to schedule systems to run at an interval
|
||||||
|
Loading…
Reference in New Issue
Block a user