27#include <fmt/ranges.h>
42 SpawnedPoolPools::const_iterator itr =
mSpawnedPools.find(pool_id);
72 return IsSpawnedObject<Creature>(db_guid_or_pool_id);
74 return IsSpawnedObject<GameObject>(db_guid_or_pool_id);
76 ABORT_MSG(
"Invalid spawn type %u passed to SpawnedPoolData::IsSpawnedObject (with spawnId " UI64FMTD ")",
uint32(type), db_guid_or_pool_id);
81void SpawnedPoolData::AddSpawn<Creature>(
uint64 db_guid,
uint32 pool_id)
88void SpawnedPoolData::AddSpawn<GameObject>(
uint64 db_guid,
uint32 pool_id)
95void SpawnedPoolData::AddSpawn<Pool>(
uint64 sub_pool_id,
uint32 pool_id)
102void SpawnedPoolData::RemoveSpawn<Creature>(
uint64 db_guid,
uint32 pool_id)
111void SpawnedPoolData::RemoveSpawn<GameObject>(
uint64 db_guid,
uint32 pool_id)
120void SpawnedPoolData::RemoveSpawn<Pool>(
uint64 sub_pool_id,
uint32 pool_id)
148 for (
PoolObject const& explicitlyChanced : ExplicitlyChanced)
149 if (!
sPoolMgr->IsEmpty(explicitlyChanced.guid))
152 for (
PoolObject const& equalChanced : EqualChanced)
153 if (!
sPoolMgr->IsEmpty(equalChanced.guid))
163 if (poolitem.
chance != 0 && maxentries == 1)
164 ExplicitlyChanced.push_back(poolitem);
166 EqualChanced.push_back(poolitem);
173 if (EqualChanced.empty())
176 for (
uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
177 chance += ExplicitlyChanced[i].chance;
178 if (chance != 100 && chance != 0)
191 Despawn1Object(spawns, guid, alwaysDeleteRespawnTime);
194 else if (alwaysDeleteRespawnTime)
195 RemoveRespawnTimeFromDB(spawns, guid);
202 DespawnObject(spawns, pooledObject.guid, alwaysDeleteRespawnTime);
204 for (
PoolObject& pooledObject : ExplicitlyChanced)
205 DespawnObject(spawns, pooledObject.guid, alwaysDeleteRespawnTime);
213 for (
auto itr = creatureBounds.first; itr != creatureBounds.second;)
223 if (alwaysDeleteRespawnTime)
232 for (
auto itr = gameobjectBounds.first; itr != gameobjectBounds.second;)
243 if (alwaysDeleteRespawnTime)
251 sPoolMgr->DespawnPool(spawns, child_pool_id, alwaysDeleteRespawnTime);
258 for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr)
260 if (itr->guid == child_pool_id)
262 ExplicitlyChanced.erase(itr);
266 for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
268 if (itr->guid == child_pool_id)
270 EqualChanced.erase(itr);
282 DespawnObject(spawns, triggerFrom);
289 candidates.reserve(EqualChanced.size() + ExplicitlyChanced.size());
294 candidates.push_back(obj);
298 candidates.push_back(obj);
300 if (candidates.empty())
304 rolledObjects.reserve(count);
307 if (!ExplicitlyChanced.empty())
312 if (candidate.chance > 0)
314 roll -= candidate.chance;
317 rolledObjects.push_back(candidate);
318 std::swap(candidate, candidates.back());
319 candidates.pop_back();
327 uint32 remainingCount = count - rolledObjects.size();
328 if (remainingCount > 0 && !candidates.empty())
330 if (candidates.size() > remainingCount)
333 rolledObjects.insert(rolledObjects.end(), candidates.begin(), candidates.end());
339 spawns.
AddSpawn<T>(objToSpawn.guid, poolId);
340 Spawn1Object(spawns, &objToSpawn);
423 TC_LOG_INFO(
"server.loading",
">> Loaded 0 object pools. DB table `pool_template` is empty.");
430 Field* fields = result->Fetch();
436 pPoolTemplate.
MapId = -1;
440 while (result->NextRow());
447 TC_LOG_INFO(
"server.loading",
"Loading Creatures Pooling Data...");
456 TC_LOG_INFO(
"server.loading",
">> Loaded 0 creatures in pools. DB table `pool_creature` is empty.");
463 Field* fields = result->Fetch();
467 float chance = fields[2].
GetFloat();
472 TC_LOG_ERROR(
"sql.sql",
"`pool_creature` has a non existing creature spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
478 TC_LOG_ERROR(
"sql.sql",
"`pool_creature` pool id ({}) is not in `pool_template`, skipped.", pool_id);
481 if (chance < 0 || chance > 100)
483 TC_LOG_ERROR(
"sql.sql",
"`pool_creature` has an invalid chance ({}) for creature guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
487 if (pPoolTemplate->
MapId == -1)
492 TC_LOG_ERROR(
"sql.sql",
"`pool_creature` has creature spawns on multiple different maps for creature guid ({}) in pool id ({}), skipped.", guid, pool_id);
505 while (result->NextRow());
513 TC_LOG_INFO(
"server.loading",
"Loading Gameobject Pooling Data...");
522 TC_LOG_INFO(
"server.loading",
">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty.");
529 Field* fields = result->Fetch();
533 float chance = fields[2].
GetFloat();
538 TC_LOG_ERROR(
"sql.sql",
"`pool_gameobject` has a non existing gameobject spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
545 TC_LOG_ERROR(
"sql.sql",
"`pool_gameobject` pool id ({}) is not in `pool_template`, skipped.", pool_id);
549 if (chance < 0 || chance > 100)
551 TC_LOG_ERROR(
"sql.sql",
"`pool_gameobject` has an invalid chance ({}) for gameobject guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
556 if (pPoolTemplate->
MapId == -1)
561 TC_LOG_ERROR(
"sql.sql",
"`pool_gameobject` has gameobject spawns on multiple different maps for gameobject guid ({}) in pool id ({}), skipped.", guid, pool_id);
574 while (result->NextRow());
582 TC_LOG_INFO(
"server.loading",
"Loading Mother Pooling Data...");
591 TC_LOG_INFO(
"server.loading",
">> Loaded 0 pools in pools");
598 Field* fields = result->Fetch();
602 float chance = fields[2].
GetFloat();
608 TC_LOG_ERROR(
"sql.sql",
"`pool_pool` mother_pool id ({}) is not in `pool_template`, skipped.", mother_pool_id);
616 TC_LOG_ERROR(
"sql.sql",
"`pool_pool` included pool_id ({}) is not in `pool_template`, skipped.", child_pool_id);
620 if (mother_pool_id == child_pool_id)
622 TC_LOG_ERROR(
"sql.sql",
"`pool_pool` pool_id ({}) includes itself, dead-lock detected, skipped.", child_pool_id);
625 if (chance < 0 || chance > 100)
627 TC_LOG_ERROR(
"sql.sql",
"`pool_pool` has an invalid chance ({}) for pool id ({}) in mother pool id ({}), skipped.", chance, child_pool_id, mother_pool_id);
640 while (result->NextRow());
646 std::set<uint32> checkedPools;
656 TC_LOG_ERROR(
"sql.sql",
"`pool_pool` has child pools on multiple maps in pool id ({}), skipped.", poolItr->second);
664 checkedPools.insert(poolItr->first);
665 if (checkedPools.find(poolItr->second) != checkedPools.end())
667 TC_LOG_ERROR(
"sql.sql",
"The pool(s) {} create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool {} and child pool {}",
668 fmt::join(checkedPools,
" "), poolItr->first, poolItr->second);
685 TC_LOG_ERROR(
"sql.sql",
"Pool Id {} is empty (has no creatures and no gameobects and either no child pools or child pools are all empty. The pool will not be spawned", poolId);
688 ASSERT(templateData.MapId != -1);
692 TC_LOG_INFO(
"server.loading",
"Starting objects pooling system...");
696 QueryResult result =
WorldDatabase.Query(
"SELECT DISTINCT pool_template.entry, pool_members.spawnId, pool_members.poolSpawnId FROM pool_template"
697 " LEFT JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry"
698 " LEFT JOIN pool_members ON pool_members.type = 2 AND pool_template.entry = pool_members.spawnId WHERE game_event_pool.pool_entry IS NULL");
702 TC_LOG_INFO(
"server.loading",
">> Pool handling system initialized, 0 pools spawned.");
709 Field* fields = result->Fetch();
721 TC_LOG_ERROR(
"sql.sql",
"Pool Id {} has no equal chance pooled entites defined and explicit chance sum is not 100. This broken pool is a child pool of Id {} and cannot be safely removed.", pool_entry, fields[2].GetUInt32());
723 TC_LOG_ERROR(
"sql.sql",
"Pool Id {} has no equal chance pooled entites defined and explicit chance sum is not 100. The pool will not be spawned.", pool_entry);
734 while (result->NextRow());
749 it->second.SpawnObject(spawnedPoolData,
mPoolTemplate[pool_id].MaxLimit, db_guid);
759 it->second.SpawnObject(spawnedPoolData,
mPoolTemplate[pool_id].MaxLimit, db_guid);
769 it->second.SpawnObject(spawnedPoolData,
mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
774 SpawnPool<Pool>(spawnedPoolData, pool_id, 0);
775 SpawnPool<GameObject>(spawnedPoolData, pool_id, 0);
776 SpawnPool<Creature>(spawnedPoolData, pool_id, 0);
785 it->second.DespawnAllObjects(spawnedPoolData, alwaysDeleteRespawnTime);
790 it->second.DespawnAllObjects(spawnedPoolData, alwaysDeleteRespawnTime);
795 it->second.DespawnAllObjects(spawnedPoolData, alwaysDeleteRespawnTime);
805 return IsPartOfAPool<Creature>(spawnId);
807 return IsPartOfAPool<GameObject>(spawnId);
811 ABORT_MSG(
"Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId " UI64FMTD ")",
uint32(type), spawnId);
863 if (
uint32 motherpoolid = IsPartOfAPool<Pool>(pool_id))
864 SpawnPool<Pool>(spawnedPoolData, motherpoolid, pool_id);
866 SpawnPool<T>(spawnedPoolData, pool_id, db_guid_or_pool_id);
878 UpdatePool<Creature>(spawnedPoolData, pool_id, spawnId);
881 UpdatePool<GameObject>(spawnedPoolData, pool_id, spawnId);
884 ABORT_MSG(
"Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId " UI64FMTD ")",
uint32(type), spawnId);
890 std::unique_ptr<SpawnedPoolData> spawnedPoolData = std::make_unique<SpawnedPoolData>(map);
892 for (
uint32 poolId : *poolIds)
895 return spawnedPoolData;
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
#define TC_LOG_DEBUG(filterType__, message__,...)
#define TC_LOG_ERROR(filterType__, message__,...)
#define TC_LOG_INFO(filterType__, message__,...)
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
static Creature * CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true, bool allowDuplicate=false)
bool GetRespawnCompatibilityMode() const
void SaveRespawnTime(uint32 forceDelay=0)
Class used to access individual fields of database query result.
float GetFloat() const noexcept
uint64 GetUInt64() const noexcept
uint32 GetUInt32() const noexcept
bool isSpawnedByDefault() const
static GameObject * CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true)
void SaveRespawnTime(uint32 forceDelay=0)
bool GetRespawnCompatibilityMode()
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr, bool alwaysDeleteFromDB=false)
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
bool IsGridLoaded(uint32 gridId) const
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
void SetPoolId(uint32 pool_id)
void DespawnObject(SpawnedPoolData &spawns, uint64 guid, bool alwaysDeleteRespawnTime=false)
void Despawn1Object(SpawnedPoolData &spawns, uint64 guid, bool alwaysDeleteRespawnTime=false, bool saveRespawnTime=true)
void RemoveOneRelation(uint32 child_pool_id)
void DespawnAllObjects(SpawnedPoolData &spawns, bool alwaysDeleteRespawnTime=false)
void Spawn1Object(SpawnedPoolData &spawns, PoolObject *obj)
void AddEntry(PoolObject &poolitem, uint32 maxentries)
bool isEmptyDeepCheck() const
void RemoveRespawnTimeFromDB(SpawnedPoolData &spawns, uint64 guid)
std::vector< PoolObject > PoolObjectList
void SpawnObject(SpawnedPoolData &spawns, uint32 limit, uint64 triggerFrom)
std::pair< uint64, uint32 > SearchPair
bool IsEmpty(uint32 pool_id) const
bool CheckPool(uint32 pool_id) const
std::unique_ptr< SpawnedPoolData > InitPoolsForMap(Map *map)
void UpdatePool(SpawnedPoolData &spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id)
static PoolMgr * instance()
void SpawnPool(SpawnedPoolData &spawnedPoolData, uint32 pool_id)
PoolTemplateData const * GetPoolTemplate(uint16 pool_id) const
PoolGroupCreatureMap mPoolCreatureGroups
PoolGroupPoolMap mPoolPoolGroups
SearchMap mGameobjectSearchMap
PoolTemplateDataMap mPoolTemplate
std::unordered_map< uint32, std::vector< uint32 > > mAutoSpawnPoolsPerMap
uint32 IsPartOfAPool(uint64 db_guid_or_pool_id) const
PoolGroupGameObjectMap mPoolGameobjectGroups
void DespawnPool(SpawnedPoolData &spawnedPoolData, uint32 pool_id, bool alwaysDeleteRespawnTime=false)
SearchMap mCreatureSearchMap
SpawnedPoolObjects mSpawnedCreatures
SpawnedPoolObjects mSpawnedGameobjects
SpawnedPoolData(Map *owner)
void AddSpawn(uint64 db_guid_or_pool_id, uint32 pool_id)
uint32 GetSpawnedObjects(uint32 pool_id) const
bool IsSpawnedObject(uint64 db_guid_or_pool_id) const
void RemoveSpawn(uint64 db_guid_or_pool_id, uint32 pool_id)
SpawnedPoolPools mSpawnedPools
void AddObjectToRemoveList()
auto MapGetValuePtr(M &map, typename M::key_type const &key)
void RandomResize(C &container, std::size_t requestedSize)
PoolObject(uint64 _guid, float _chance)