From cff94b6c225ab3d92408b63e4541d2cea5a419a1 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sat, 21 Jan 2023 07:37:09 -0800 Subject: [PATCH] Fix hash collisions in achievements (#962) --- dGame/dComponents/AchievementCacheKey.h | 43 +++++++++++++++++++++++++ dGame/dComponents/MissionComponent.cpp | 19 ++++++----- dGame/dComponents/MissionComponent.h | 14 ++++---- 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 dGame/dComponents/AchievementCacheKey.h diff --git a/dGame/dComponents/AchievementCacheKey.h b/dGame/dComponents/AchievementCacheKey.h new file mode 100644 index 00000000..d8892197 --- /dev/null +++ b/dGame/dComponents/AchievementCacheKey.h @@ -0,0 +1,43 @@ +#ifndef __ACHIEVEMENTCACHEKEY__H__ +#define __ACHIEVEMENTCACHEKEY__H__ + +class AchievementCacheKey { +public: + AchievementCacheKey() { + targets = ""; + value = 0; + type = MissionTaskType::MISSION_TASK_TYPE_UNKNOWN; + }; + + bool operator==(const AchievementCacheKey& point) const { + return this->targets == point.targets && this->value == point.value && this->type == point.type; + }; + void SetTargets(const std::string value) { this->targets = value; }; + void SetValue(uint32_t value) { this->value = value; }; + void SetType(MissionTaskType value) { this->type = value; }; + + std::string GetTargets() const { return this->targets; }; + uint32_t GetValue() const { return this->value; }; + MissionTaskType GetType() const { return this->type; }; +private: + std::string targets; + uint32_t value; + MissionTaskType type; + +}; + +// Specialization of hash for the above class +namespace std { + template<> + struct hash { + size_t operator()(const AchievementCacheKey& key) const { + size_t hash = 0; + GeneralUtils::hash_combine(hash, key.GetType()); + GeneralUtils::hash_combine(hash, key.GetValue()); + GeneralUtils::hash_combine(hash, key.GetTargets()); + return hash; + }; + }; +}; + +#endif //!__ACHIEVEMENTCACHEKEY__H__ diff --git a/dGame/dComponents/MissionComponent.cpp b/dGame/dComponents/MissionComponent.cpp index 96f213e5..2ef6c2f6 100644 --- a/dGame/dComponents/MissionComponent.cpp +++ b/dGame/dComponents/MissionComponent.cpp @@ -17,10 +17,11 @@ #include "dZoneManager.h" #include "Mail.h" #include "MissionPrerequisites.h" +#include "AchievementCacheKey.h" // MARK: Mission Component -std::unordered_map> MissionComponent::m_AchievementCache = {}; +std::unordered_map> MissionComponent::m_AchievementCache = {}; //! Initializer MissionComponent::MissionComponent(Entity* parent) : Component(parent) { @@ -391,12 +392,12 @@ bool MissionComponent::LookForAchievements(MissionTaskType type, int32_t value, const std::vector& MissionComponent::QueryAchievements(MissionTaskType type, int32_t value, const std::string targets) { // Create a hash which represent this query for achievements - size_t hash = 0; - GeneralUtils::hash_combine(hash, type); - GeneralUtils::hash_combine(hash, value); - GeneralUtils::hash_combine(hash, targets); + AchievementCacheKey toFind; + toFind.SetType(type); + toFind.SetValue(value); + toFind.SetTargets(targets); - const std::unordered_map>::iterator& iter = m_AchievementCache.find(hash); + const auto& iter = m_AchievementCache.find(toFind); // Check if this query is cached if (iter != m_AchievementCache.end()) { @@ -447,11 +448,9 @@ const std::vector& MissionComponent::QueryAchievements(MissionTaskType } } } - // Insert into cache - m_AchievementCache.insert_or_assign(hash, result); - - return m_AchievementCache.find(hash)->second; + m_AchievementCache.insert_or_assign(toFind, result); + return m_AchievementCache.find(toFind)->second; } bool MissionComponent::RequiresItem(const LOT lot) { diff --git a/dGame/dComponents/MissionComponent.h b/dGame/dComponents/MissionComponent.h index 58185a68..a3e39a88 100644 --- a/dGame/dComponents/MissionComponent.h +++ b/dGame/dComponents/MissionComponent.h @@ -17,11 +17,13 @@ #include "CDMissionsTable.h" #include "Component.h" - /** - * The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for - * progression of each of the mission task types (see MissionTaskType). - */ -class MissionComponent : public Component +class AchievementCacheKey; + +/** + * The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for + * progression of each of the mission task types (see MissionTaskType). + */ +class MissionComponent: public Component { public: static const uint32_t ComponentType = COMPONENT_TYPE_MISSION; @@ -192,7 +194,7 @@ private: * As achievements can be hard to query, we here store a list of all the mission IDs that can be unlocked for a * combination of tasks and values, so that they can be easily re-queried later */ - static std::unordered_map> m_AchievementCache; + static std::unordered_map> m_AchievementCache; /** * Order of missions in the UI. This value is incremented by 1