mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Pooling: Quest pooling rewrite: (PR#23627)
- Split quest pooling from PoolMgr (into QuestPoolMgr)
- Proper saving/restoring on server restart
- No more hacking into sObjectMgr to insert/remove available quests
(cherry picked from commit a5e73e41c0)
This commit is contained in:
@@ -60,13 +60,6 @@ TC_GAME_API bool ActivePoolData::IsActiveObject<Pool>(uint64 sub_pool_id) const
|
||||
return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end();
|
||||
}
|
||||
|
||||
// Method that tell if a quest can be started
|
||||
template<>
|
||||
TC_GAME_API bool ActivePoolData::IsActiveObject<Quest>(uint64 quest_id) const
|
||||
{
|
||||
return mActiveQuests.find(quest_id) != mActiveQuests.end();
|
||||
}
|
||||
|
||||
template<>
|
||||
void ActivePoolData::ActivateObject<Creature>(uint64 db_guid, uint32 pool_id)
|
||||
{
|
||||
@@ -88,13 +81,6 @@ void ActivePoolData::ActivateObject<Pool>(uint64 sub_pool_id, uint32 pool_id)
|
||||
++mSpawnedPools[pool_id];
|
||||
}
|
||||
|
||||
template<>
|
||||
void ActivePoolData::ActivateObject<Quest>(uint64 quest_id, uint32 pool_id)
|
||||
{
|
||||
mActiveQuests.insert(quest_id);
|
||||
++mSpawnedPools[pool_id];
|
||||
}
|
||||
|
||||
template<>
|
||||
void ActivePoolData::RemoveObject<Creature>(uint64 db_guid, uint32 pool_id)
|
||||
{
|
||||
@@ -122,15 +108,6 @@ void ActivePoolData::RemoveObject<Pool>(uint64 sub_pool_id, uint32 pool_id)
|
||||
--val;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ActivePoolData::RemoveObject<Quest>(uint64 quest_id, uint32 pool_id)
|
||||
{
|
||||
mActiveQuests.erase(quest_id);
|
||||
uint32& val = mSpawnedPools[pool_id];
|
||||
if (val > 0)
|
||||
--val;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Methods of template class PoolGroup
|
||||
|
||||
@@ -250,49 +227,6 @@ void PoolGroup<Pool>::Despawn1Object(uint64 child_pool_id)
|
||||
sPoolMgr->DespawnPool(child_pool_id);
|
||||
}
|
||||
|
||||
// Same on one quest
|
||||
template<>
|
||||
void PoolGroup<Quest>::Despawn1Object(uint64 quest_id)
|
||||
{
|
||||
// Creatures
|
||||
QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap();
|
||||
PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(quest_id);
|
||||
for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
|
||||
{
|
||||
QuestRelations::iterator qitr = questMap->find(itr->second);
|
||||
if (qitr == questMap->end())
|
||||
continue;
|
||||
QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
|
||||
for (; qitr != lastElement; ++qitr)
|
||||
{
|
||||
if (qitr->first == itr->second && qitr->second == itr->first)
|
||||
{
|
||||
questMap->erase(qitr); // iterator is now no more valid
|
||||
break; // but we can exit loop since the element is found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gameobjects
|
||||
questMap = sObjectMgr->GetGOQuestRelationMap();
|
||||
qr = sPoolMgr->mQuestGORelation.equal_range(quest_id);
|
||||
for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
|
||||
{
|
||||
QuestRelations::iterator qitr = questMap->find(itr->second);
|
||||
if (qitr == questMap->end())
|
||||
continue;
|
||||
QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
|
||||
for (; qitr != lastElement; ++qitr)
|
||||
{
|
||||
if (qitr->first == itr->second && qitr->second == itr->first)
|
||||
{
|
||||
questMap->erase(qitr); // iterator is now no more valid
|
||||
break; // but we can exit loop since the element is found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method for a pool only to remove any found record causing a circular dependency loop
|
||||
template<>
|
||||
void PoolGroup<Pool>::RemoveOneRelation(uint32 child_pool_id)
|
||||
@@ -428,100 +362,6 @@ void PoolGroup<Pool>::Spawn1Object(PoolObject* obj)
|
||||
sPoolMgr->SpawnPool(obj->guid);
|
||||
}
|
||||
|
||||
// Same for 1 quest
|
||||
template<>
|
||||
void PoolGroup<Quest>::Spawn1Object(PoolObject* obj)
|
||||
{
|
||||
// Creatures
|
||||
QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap();
|
||||
PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(obj->guid);
|
||||
for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
|
||||
{
|
||||
TC_LOG_DEBUG("pool", "PoolGroup<Quest>: Adding quest %u to creature %u", itr->first, itr->second);
|
||||
questMap->insert(QuestRelations::value_type(itr->second, itr->first));
|
||||
}
|
||||
|
||||
// Gameobjects
|
||||
questMap = sObjectMgr->GetGOQuestRelationMap();
|
||||
qr = sPoolMgr->mQuestGORelation.equal_range(obj->guid);
|
||||
for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
|
||||
{
|
||||
TC_LOG_DEBUG("pool", "PoolGroup<Quest>: Adding quest %u to GO %u", itr->first, itr->second);
|
||||
questMap->insert(QuestRelations::value_type(itr->second, itr->first));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void PoolGroup<Quest>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint64 triggerFrom)
|
||||
{
|
||||
TC_LOG_DEBUG("pool", "PoolGroup<Quest>: Spawning pool %u", poolId);
|
||||
// load state from db
|
||||
if (!triggerFrom)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_POOL_QUEST_SAVE);
|
||||
|
||||
stmt->setUInt32(0, poolId);
|
||||
|
||||
PreparedQueryResult result = CharacterDatabase.Query(stmt);
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
uint32 questId = result->Fetch()[0].GetUInt32();
|
||||
spawns.ActivateObject<Quest>(questId, poolId);
|
||||
PoolObject tempObj(questId, 0.0f);
|
||||
Spawn1Object(&tempObj);
|
||||
--limit;
|
||||
} while (result->NextRow() && limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ActivePoolObjects currentQuests = spawns.GetActiveQuests();
|
||||
ActivePoolObjects newQuests;
|
||||
|
||||
// always try to select different quests
|
||||
for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
|
||||
{
|
||||
if (spawns.IsActiveObject<Quest>(itr->guid))
|
||||
continue;
|
||||
newQuests.insert(itr->guid);
|
||||
}
|
||||
|
||||
// clear the pool
|
||||
DespawnObject(spawns);
|
||||
|
||||
// recycle minimal amount of quests if possible count is lower than limit
|
||||
if (limit > newQuests.size() && !currentQuests.empty())
|
||||
{
|
||||
do
|
||||
{
|
||||
uint32 questId = Trinity::Containers::SelectRandomContainerElement(currentQuests);
|
||||
newQuests.insert(questId);
|
||||
currentQuests.erase(questId);
|
||||
} while (newQuests.size() < limit && !currentQuests.empty()); // failsafe
|
||||
}
|
||||
|
||||
if (newQuests.empty())
|
||||
return;
|
||||
|
||||
// activate <limit> random quests
|
||||
do
|
||||
{
|
||||
uint32 questId = Trinity::Containers::SelectRandomContainerElement(newQuests);
|
||||
spawns.ActivateObject<Quest>(questId, poolId);
|
||||
PoolObject tempObj(questId, 0.0f);
|
||||
Spawn1Object(&tempObj);
|
||||
newQuests.erase(questId);
|
||||
--limit;
|
||||
} while (limit && !newQuests.empty());
|
||||
|
||||
// if we are here it means the pool is initialized at startup and did not have previous saved state
|
||||
if (!triggerFrom)
|
||||
sPoolMgr->SaveQuestsToDB();
|
||||
}
|
||||
|
||||
// Method that does the respawn job on the specified creature
|
||||
template <>
|
||||
void PoolGroup<Creature>::ReSpawn1Object(PoolObject* /*obj*/)
|
||||
@@ -540,10 +380,6 @@ void PoolGroup<GameObject>::ReSpawn1Object(PoolObject* /*obj*/)
|
||||
template <>
|
||||
void PoolGroup<Pool>::ReSpawn1Object(PoolObject* /*obj*/) { }
|
||||
|
||||
// Nothing to do for a quest
|
||||
template <>
|
||||
void PoolGroup<Quest>::ReSpawn1Object(PoolObject* /*obj*/) { }
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Methods of class PoolMgr
|
||||
|
||||
@@ -551,7 +387,6 @@ PoolMgr::PoolMgr() { }
|
||||
|
||||
void PoolMgr::Initialize()
|
||||
{
|
||||
mQuestSearchMap.clear();
|
||||
mGameobjectSearchMap.clear();
|
||||
mCreatureSearchMap.clear();
|
||||
}
|
||||
@@ -811,95 +646,6 @@ void PoolMgr::LoadFromDB()
|
||||
}
|
||||
}
|
||||
|
||||
TC_LOG_INFO("server.loading", "Loading Quest Pooling Data...");
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_QUEST_POOLS);
|
||||
PreparedQueryResult result = WorldDatabase.Query(stmt);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 quests in pools");
|
||||
}
|
||||
else
|
||||
{
|
||||
PooledQuestRelationBounds creBounds;
|
||||
PooledQuestRelationBounds goBounds;
|
||||
|
||||
enum eQuestTypes
|
||||
{
|
||||
QUEST_NONE = 0,
|
||||
QUEST_DAILY = 1,
|
||||
QUEST_WEEKLY = 2
|
||||
};
|
||||
|
||||
std::map<uint32, int32> poolTypeMap;
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 entry = fields[0].GetUInt32();
|
||||
uint32 pool_id = fields[1].GetUInt32();
|
||||
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(entry);
|
||||
if (!quest)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = mPoolTemplate.find(pool_id);
|
||||
if (it == mPoolTemplate.end())
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "`pool_quest` pool id (%u) is not in `pool_template`, skipped.", pool_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!quest->IsDailyOrWeekly())
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "`pool_quest` has an quest (%u) which is not daily or weekly in pool id (%u), use ExclusiveGroup instead, skipped.", entry, pool_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (poolTypeMap[pool_id] == QUEST_NONE)
|
||||
poolTypeMap[pool_id] = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY;
|
||||
|
||||
int32 currType = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY;
|
||||
|
||||
if (poolTypeMap[pool_id] != currType)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.",
|
||||
entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY");
|
||||
continue;
|
||||
}
|
||||
|
||||
creBounds = mQuestCreatureRelation.equal_range(entry);
|
||||
goBounds = mQuestGORelation.equal_range(entry);
|
||||
|
||||
if (creBounds.first == creBounds.second && goBounds.first == goBounds.second)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
|
||||
PoolObject plObject = PoolObject(entry, 0.0f);
|
||||
PoolGroup<Quest>& questgroup = mPoolQuestGroups[pool_id];
|
||||
questgroup.SetPoolId(pool_id);
|
||||
questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
|
||||
SearchPair p(entry, pool_id);
|
||||
mQuestSearchMap.insert(p);
|
||||
|
||||
++count;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
TC_LOG_INFO("server.loading", ">> Loaded %u quests in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
}
|
||||
|
||||
// The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks
|
||||
TC_LOG_INFO("server.loading", "Starting objects pooling system...");
|
||||
{
|
||||
@@ -948,70 +694,6 @@ void PoolMgr::LoadFromDB()
|
||||
}
|
||||
}
|
||||
|
||||
void PoolMgr::LoadQuestPools()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PoolMgr::SaveQuestsToDB()
|
||||
{
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
|
||||
{
|
||||
if (itr->second.isEmpty())
|
||||
continue;
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_POOL_SAVE);
|
||||
stmt->setUInt32(0, itr->second.GetPoolId());
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
for (SearchMap::iterator itr = mQuestSearchMap.begin(); itr != mQuestSearchMap.end(); ++itr)
|
||||
{
|
||||
if (IsSpawnedObject<Quest>(itr->first))
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_POOL_SAVE);
|
||||
stmt->setUInt32(0, itr->second);
|
||||
stmt->setUInt32(1, itr->first);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
void PoolMgr::ChangeDailyQuests()
|
||||
{
|
||||
for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
|
||||
{
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->second.GetFirstEqualChancedObjectId()))
|
||||
{
|
||||
if (quest->IsWeekly())
|
||||
continue;
|
||||
|
||||
UpdatePool<Quest>(itr->second.GetPoolId(), 1); // anything non-zero means don't load from db
|
||||
}
|
||||
}
|
||||
|
||||
SaveQuestsToDB();
|
||||
}
|
||||
|
||||
void PoolMgr::ChangeWeeklyQuests()
|
||||
{
|
||||
for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
|
||||
{
|
||||
if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->second.GetFirstEqualChancedObjectId()))
|
||||
{
|
||||
if (quest->IsDaily())
|
||||
continue;
|
||||
|
||||
UpdatePool<Quest>(itr->second.GetPoolId(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
SaveQuestsToDB();
|
||||
}
|
||||
|
||||
// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
|
||||
// If it's same, the creature is respawned only (added back to map)
|
||||
template<>
|
||||
@@ -1042,21 +724,11 @@ void PoolMgr::SpawnPool<Pool>(uint32 pool_id, uint64 sub_pool_id)
|
||||
it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
|
||||
}
|
||||
|
||||
// Call to spawn a pool
|
||||
template<>
|
||||
void PoolMgr::SpawnPool<Quest>(uint32 pool_id, uint64 quest_id)
|
||||
{
|
||||
auto it = mPoolQuestGroups.find(pool_id);
|
||||
if (it != mPoolQuestGroups.end() && !it->second.isEmpty())
|
||||
it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, quest_id);
|
||||
}
|
||||
|
||||
void PoolMgr::SpawnPool(uint32 pool_id)
|
||||
{
|
||||
SpawnPool<Pool>(pool_id, 0);
|
||||
SpawnPool<GameObject>(pool_id, 0);
|
||||
SpawnPool<Creature>(pool_id, 0);
|
||||
SpawnPool<Quest>(pool_id, 0);
|
||||
}
|
||||
|
||||
// Call to despawn a pool, all gameobjects/creatures in this pool are removed
|
||||
@@ -1077,11 +749,6 @@ void PoolMgr::DespawnPool(uint32 pool_id)
|
||||
if (it != mPoolPoolGroups.end() && !it->second.isEmpty())
|
||||
it->second.DespawnObject(mSpawnedData);
|
||||
}
|
||||
{
|
||||
auto it = mPoolQuestGroups.find(pool_id);
|
||||
if (it != mPoolQuestGroups.end() && !it->second.isEmpty())
|
||||
it->second.DespawnObject(mSpawnedData);
|
||||
}
|
||||
}
|
||||
|
||||
// Selects proper template overload to call based on passed type
|
||||
@@ -1117,11 +784,6 @@ bool PoolMgr::CheckPool(uint32 pool_id) const
|
||||
if (it != mPoolPoolGroups.end() && !it->second.CheckPool())
|
||||
return false;
|
||||
}
|
||||
{
|
||||
auto it = mPoolQuestGroups.find(pool_id);
|
||||
if (it != mPoolQuestGroups.end() && !it->second.CheckPool())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1140,4 +802,3 @@ void PoolMgr::UpdatePool(uint32 pool_id, uint64 db_guid_or_pool_id)
|
||||
template void PoolMgr::UpdatePool<Pool>(uint32 pool_id, uint64 db_guid_or_pool_id);
|
||||
template void PoolMgr::UpdatePool<GameObject>(uint32 pool_id, uint64 db_guid_or_pool_id);
|
||||
template void PoolMgr::UpdatePool<Creature>(uint32 pool_id, uint64 db_guid_or_pool_id);
|
||||
template void PoolMgr::UpdatePool<Quest>(uint32 pool_id, uint64 db_guid_or_pool_id);
|
||||
|
||||
Reference in New Issue
Block a user