Core/Achievements: port realm first achievement handling from master
veeeery partial cherry-pick of c75fcbe20b07fd7518be58bf02ba30d903133449 (cherry picked from commit 4596c17dae1dcb199b9301b7dcfd556bb1c0d7d5)
This commit is contained in:
1 parent
62a7c88ef6
commit
8aa53f2df7
4 files changed
+48
-42
No files matched your search
@@ -1549,7 +1549,7 @@ bool AchievementMgr<T>::IsCompletedCriteria(AchievementCriteriaEntry const* achi
|
||||
if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_GUILD))
|
||||
{
|
||||
// someone on this realm has already completed that achievement
|
||||
if (sAchievementMgr->IsRealmCompleted(achievement, GetInstanceId(GetOwner())))
|
||||
if (sAchievementMgr->IsRealmCompleted(achievement))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1930,7 +1930,8 @@ void AchievementMgr<Player>::CompletedAchievement(AchievementEntry const* achiev
|
||||
ca.date = GameTime::GetGameTime();
|
||||
ca.changed = true;
|
||||
|
||||
sAchievementMgr->SetRealmCompleted(achievement, GetOwner()->GetInstanceId());
|
||||
if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
|
||||
sAchievementMgr->SetRealmCompleted(achievement);
|
||||
|
||||
_achievementPoints += achievement->Points;
|
||||
|
||||
@@ -2023,7 +2024,7 @@ void AchievementMgr<Guild>::CompletedAchievement(AchievementEntry const* achieve
|
||||
ca.guids.insert(groupMember->GetGUID());
|
||||
}
|
||||
|
||||
sAchievementMgr->SetRealmCompleted(achievement, referencePlayer->GetInstanceId());
|
||||
sAchievementMgr->SetRealmCompleted(achievement);
|
||||
|
||||
_achievementPoints += achievement->Points;
|
||||
|
||||
@@ -3322,6 +3323,32 @@ AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteria
|
||||
}
|
||||
}
|
||||
|
||||
bool AchievementGlobalMgr::IsRealmCompleted(AchievementEntry const* achievement) const
|
||||
{
|
||||
auto itr = _allCompletedAchievements.find(achievement->ID);
|
||||
if (itr == _allCompletedAchievements.end())
|
||||
return false;
|
||||
|
||||
if (itr->second == std::chrono::system_clock::time_point::min())
|
||||
return false;
|
||||
|
||||
// Allow completing the realm first kill for entire minute after first person did it
|
||||
// it may allow more than one group to achieve it (highly unlikely)
|
||||
// but apparently this is how blizz handles it as well
|
||||
if (achievement->Flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)
|
||||
return (GameTime::GetGameTimeSystemPoint() - itr->second) > Minutes(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AchievementGlobalMgr::SetRealmCompleted(AchievementEntry const* achievement)
|
||||
{
|
||||
if (IsRealmCompleted(achievement))
|
||||
return;
|
||||
|
||||
_allCompletedAchievements[achievement->ID] = GameTime::GetGameTimeSystemPoint();
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
void AchievementGlobalMgr::LoadAchievementCriteriaList()
|
||||
{
|
||||
@@ -3506,6 +3533,14 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// Populate _allCompletedAchievements with all realm first achievement ids to make multithreaded access safer
|
||||
// while it will not prevent races, it will prevent crashes that happen because std::unordered_map key was added
|
||||
// instead the only potential race will happen on value associated with the key
|
||||
for (uint32 i = 0; i < sAchievementStore.GetNumRows(); ++i)
|
||||
if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(i))
|
||||
if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
|
||||
_allCompletedAchievements[achievement->ID] = std::chrono::system_clock::time_point::min();
|
||||
|
||||
QueryResult result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
|
||||
|
||||
if (!result)
|
||||
@@ -3532,11 +3567,10 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
|
||||
continue;
|
||||
}
|
||||
else if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_GUILD))
|
||||
m_allCompletedAchievements[achievementId] = uint32(0xFFFFFFFF);
|
||||
}
|
||||
while (result->NextRow());
|
||||
_allCompletedAchievements[achievementId] = std::chrono::system_clock::time_point::max();;
|
||||
} while (result->NextRow());
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms.", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms.", (unsigned long)_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void AchievementGlobalMgr::LoadRewards()
|
||||
@@ -3706,10 +3740,3 @@ AchievementCriteriaEntry const* AchievementGlobalMgr::GetAchievementCriteria(uin
|
||||
{
|
||||
return sAchievementCriteriaStore.LookupEntry(criteriaId);
|
||||
}
|
||||
|
||||
void AchievementGlobalMgr::OnInstanceDestroyed(uint32 instanceId)
|
||||
{
|
||||
for (auto& realmCompletion : m_allCompletedAchievements)
|
||||
if (realmCompletion.second == instanceId)
|
||||
realmCompletion.second = uint32(0xFFFFFFFF);
|
||||
}
|
||||
@@ -360,26 +360,6 @@ class TC_GAME_API AchievementGlobalMgr
|
||||
return iter != m_criteriaDataMap.end() ? &iter->second : nullptr;
|
||||
}
|
||||
|
||||
bool IsRealmCompleted(AchievementEntry const* achievement, uint32 instanceId) const
|
||||
{
|
||||
AllCompletedAchievements::const_iterator itr = m_allCompletedAchievements.find(achievement->ID);
|
||||
if (itr == m_allCompletedAchievements.end())
|
||||
return false;
|
||||
|
||||
if (achievement->Flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)
|
||||
return itr->second != instanceId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetRealmCompleted(AchievementEntry const* achievement, uint32 instanceId)
|
||||
{
|
||||
if (IsRealmCompleted(achievement, instanceId))
|
||||
return;
|
||||
|
||||
m_allCompletedAchievements[achievement->ID] = instanceId;
|
||||
}
|
||||
|
||||
bool IsGroupCriteriaType(AchievementCriteriaTypes type) const
|
||||
{
|
||||
switch (type)
|
||||
@@ -397,9 +377,8 @@ class TC_GAME_API AchievementGlobalMgr
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes instanceId as valid id to complete realm first kill achievements
|
||||
void OnInstanceDestroyed(uint32 instanceId);
|
||||
bool IsRealmCompleted(AchievementEntry const* achievement) const;
|
||||
void SetRealmCompleted(AchievementEntry const* achievement);
|
||||
|
||||
void LoadAchievementCriteriaList();
|
||||
void LoadAchievementCriteriaData();
|
||||
@@ -430,8 +409,10 @@ class TC_GAME_API AchievementGlobalMgr
|
||||
// store achievements by referenced achievement id to speed up lookup
|
||||
AchievementListByReferencedId m_AchievementListByReferencedId;
|
||||
|
||||
typedef std::map<uint32 /*achievementId*/, uint32 /*instanceId*/> AllCompletedAchievements;
|
||||
AllCompletedAchievements m_allCompletedAchievements;
|
||||
// store realm first achievements
|
||||
// std::chrono::system_clock::time_point::min() is a placeholder value for realm firsts not yet completed
|
||||
// std::chrono::system_clock::time_point::max() is a value assigned to realm firsts complete before worldserver started
|
||||
std::unordered_map<uint32 /*achievementId*/, std::chrono::system_clock::time_point /*completionTime*/> _allCompletedAchievements;
|
||||
|
||||
AchievementRewards m_achievementRewards;
|
||||
AchievementRewardLocales m_achievementRewardLocales;
|
||||
|
||||
@@ -449,7 +449,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
|
||||
case CONDITION_REALM_ACHIEVEMENT:
|
||||
{
|
||||
AchievementEntry const* achievement = sAchievementMgr->GetAchievement(ConditionValue1);
|
||||
if (achievement && sAchievementMgr->IsRealmCompleted(achievement, std::numeric_limits<uint32>::max()))
|
||||
if (achievement && sAchievementMgr->IsRealmCompleted(achievement))
|
||||
condMeets = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "Player.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Opcodes.h"
|
||||
#include "AchievementMgr.h"
|
||||
|
||||
MapManager::MapManager()
|
||||
: _nextInstanceId(0), _scheduledScripts(0)
|
||||
@@ -395,5 +394,4 @@ void MapManager::FreeInstanceId(uint32 instanceId)
|
||||
// If freed instance id is lower than the next id available for new instances, use the freed one instead
|
||||
_nextInstanceId = std::min(instanceId, _nextInstanceId);
|
||||
_freeInstanceIds[instanceId] = true;
|
||||
sAchievementMgr->OnInstanceDestroyed(instanceId);
|
||||
}
|
||||
Reference in new issue
Block a user