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:
ariel- authored and funjoker committed 2022-01-28 22:32:59 +01:00
1 parent 62a7c88ef6
commit 8aa53f2df7
4 files changed
+48 -42

No files matched your search

+41 -14
View File
@@ -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);
}
+6 -25
View File
@@ -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;
+1 -1
View File
@@ -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;
}
-2
View File
@@ -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);
}