Core/Achievements: port realm first achievement handling from master

veeeery partial cherry-pick of c75fcbe20b
This commit is contained in:
ariel-
2017-03-02 01:07:05 -03:00
parent 7072bf8c02
commit 4596c17dae
4 changed files with 47 additions and 40 deletions

View File

@@ -1130,7 +1130,7 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
{
// someone on this realm has already completed that achievement
if (sAchievementMgr->IsRealmCompleted(achievement, GetPlayer()->GetInstanceId()))
if (sAchievementMgr->IsRealmCompleted(achievement))
return false;
}
@@ -1494,7 +1494,8 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
ca.date = time(NULL);
ca.changed = true;
sAchievementMgr->SetRealmCompleted(achievement, GetPlayer()->GetInstanceId());
if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
sAchievementMgr->SetRealmCompleted(achievement);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, achievement->ID);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points);
@@ -2245,6 +2246,32 @@ AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteria
return m_AchievementCriteriasByType[type];
}
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()
{
@@ -2488,6 +2515,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)
@@ -2514,11 +2549,10 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
continue;
}
else if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
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()
@@ -2691,10 +2725,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);
}

View File

@@ -369,28 +369,8 @@ class TC_GAME_API AchievementGlobalMgr
return iter != m_criteriaDataMap.end() ? &iter->second : NULL;
}
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;
}
// 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();
@@ -419,8 +399,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;

View File

@@ -431,7 +431,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;
}

View File

@@ -35,7 +35,6 @@
#include "Player.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "AchievementMgr.h"
MapManager::MapManager()
: _nextInstanceId(0), _scheduledScripts(0)
@@ -370,5 +369,4 @@ void MapManager::FreeInstanceId(uint32 instanceId)
SetNextInstanceId(instanceId);
_instanceIds[instanceId] = false;
sAchievementMgr->OnInstanceDestroyed(instanceId);
}