mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-21 17:54:48 +01:00
Core/Achievements: port realm first achievement handling from master
veeeery partial cherry-pick of c75fcbe20b
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user