TrinityCore
PoolMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "PoolMgr.h"
19#include "Containers.h"
20#include "Creature.h"
21#include "DatabaseEnv.h"
22#include "GameObject.h"
23#include "Log.h"
24#include "Map.h"
25#include "ObjectMgr.h"
26#include <sstream>
27
28PoolObject::PoolObject(uint64 _guid, float _chance) : guid(_guid), chance(std::fabs(_chance))
29{
30}
31
33// template class SpawnedPoolData
34
35SpawnedPoolData::SpawnedPoolData(Map* owner) : mOwner(owner) { }
37
38// Method that tell amount spawned objects/subpools
40{
41 SpawnedPoolPools::const_iterator itr = mSpawnedPools.find(pool_id);
42 return itr != mSpawnedPools.end() ? itr->second : 0;
43}
44
45// Method that tell if a creature is spawned currently
46template<>
47TC_GAME_API bool SpawnedPoolData::IsSpawnedObject<Creature>(uint64 db_guid) const
48{
49 return mSpawnedCreatures.find(db_guid) != mSpawnedCreatures.end();
50}
51
52// Method that tell if a gameobject is spawned currently
53template<>
54TC_GAME_API bool SpawnedPoolData::IsSpawnedObject<GameObject>(uint64 db_guid) const
55{
56 return mSpawnedGameobjects.find(db_guid) != mSpawnedGameobjects.end();
57}
58
59// Method that tell if a pool is spawned currently
60template<>
61TC_GAME_API bool SpawnedPoolData::IsSpawnedObject<Pool>(uint64 sub_pool_id) const
62{
63 return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end();
64}
65
66bool SpawnedPoolData::IsSpawnedObject(SpawnObjectType type, uint64 db_guid_or_pool_id) const
67{
68 switch (type)
69 {
71 return IsSpawnedObject<Creature>(db_guid_or_pool_id);
73 return IsSpawnedObject<GameObject>(db_guid_or_pool_id);
74 default:
75 ABORT_MSG("Invalid spawn type %u passed to SpawnedPoolData::IsSpawnedObject (with spawnId " UI64FMTD ")", uint32(type), db_guid_or_pool_id);
76 }
77}
78
79template<>
80void SpawnedPoolData::AddSpawn<Creature>(uint64 db_guid, uint32 pool_id)
81{
82 mSpawnedCreatures.insert(db_guid);
83 ++mSpawnedPools[pool_id];
84}
85
86template<>
87void SpawnedPoolData::AddSpawn<GameObject>(uint64 db_guid, uint32 pool_id)
88{
89 mSpawnedGameobjects.insert(db_guid);
90 ++mSpawnedPools[pool_id];
92
93template<>
94void SpawnedPoolData::AddSpawn<Pool>(uint64 sub_pool_id, uint32 pool_id)
95{
96 mSpawnedPools[sub_pool_id] = 0;
97 ++mSpawnedPools[pool_id];
98}
99
100template<>
101void SpawnedPoolData::RemoveSpawn<Creature>(uint64 db_guid, uint32 pool_id)
103 mSpawnedCreatures.erase(db_guid);
104 uint32& val = mSpawnedPools[pool_id];
105 if (val > 0)
106 --val;
107}
108
109template<>
110void SpawnedPoolData::RemoveSpawn<GameObject>(uint64 db_guid, uint32 pool_id)
111{
112 mSpawnedGameobjects.erase(db_guid);
113 uint32& val = mSpawnedPools[pool_id];
114 if (val > 0)
115 --val;
116}
117
118template<>
119void SpawnedPoolData::RemoveSpawn<Pool>(uint64 sub_pool_id, uint32 pool_id)
120{
121 mSpawnedPools.erase(sub_pool_id);
122 uint32& val = mSpawnedPools[pool_id];
123 if (val > 0)
124 --val;
125}
126
128// Methods of template class PoolGroup
129
130template <class T>
132{
133}
134
135template <class T>
136PoolGroup<T>::~PoolGroup() = default;
137
138template <class T>
140{
141 return isEmpty();
142}
143
144template <>
146{
147 for (PoolObject const& explicitlyChanced : ExplicitlyChanced)
148 if (!sPoolMgr->IsEmpty(explicitlyChanced.guid))
149 return false;
150
151 for (PoolObject const& equalChanced : EqualChanced)
152 if (!sPoolMgr->IsEmpty(equalChanced.guid))
153 return false;
154
155 return true;
156}
157
158// Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
159template <class T>
160void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
161{
162 if (poolitem.chance != 0 && maxentries == 1)
163 ExplicitlyChanced.push_back(poolitem);
164 else
165 EqualChanced.push_back(poolitem);
166}
167
168// Method to check the chances are proper in this object pool
169template <class T>
171{
172 if (EqualChanced.empty())
173 {
174 float chance = 0;
175 for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
176 chance += ExplicitlyChanced[i].chance;
177 if (chance != 100 && chance != 0)
178 return false;
179 }
180 return true;
181}
182
183// Main method to despawn a creature or gameobject in a pool
184// If no guid is passed, the pool is just removed (event end case)
185// If guid is filled, cache will be used and no removal will occur, it just fill the cache
186template<class T>
187void PoolGroup<T>::DespawnObject(SpawnedPoolData& spawns, uint64 guid, bool alwaysDeleteRespawnTime)
188{
189 for (size_t i=0; i < EqualChanced.size(); ++i)
190 {
191 // if spawned
192 if (spawns.IsSpawnedObject<T>(EqualChanced[i].guid))
193 {
194 if (!guid || EqualChanced[i].guid == guid)
195 {
196 Despawn1Object(spawns, EqualChanced[i].guid, alwaysDeleteRespawnTime);
197 spawns.RemoveSpawn<T>(EqualChanced[i].guid, poolId);
198 }
199 }
200 else if (alwaysDeleteRespawnTime)
201 RemoveRespawnTimeFromDB(spawns, EqualChanced[i].guid);
202 }
203
204 for (size_t i = 0; i < ExplicitlyChanced.size(); ++i)
205 {
206 // spawned
207 if (spawns.IsSpawnedObject<T>(ExplicitlyChanced[i].guid))
208 {
209 if (!guid || ExplicitlyChanced[i].guid == guid)
210 {
211 Despawn1Object(spawns, ExplicitlyChanced[i].guid, alwaysDeleteRespawnTime);
212 spawns.RemoveSpawn<T>(ExplicitlyChanced[i].guid, poolId);
213 }
214 }
215 else if (alwaysDeleteRespawnTime)
216 RemoveRespawnTimeFromDB(spawns, ExplicitlyChanced[i].guid);
217 }
218}
219
220// Method that is actualy doing the removal job on one creature
221template<>
222void PoolGroup<Creature>::Despawn1Object(SpawnedPoolData& spawns, uint64 guid, bool alwaysDeleteRespawnTime, bool saveRespawnTime)
223{
224 auto creatureBounds = spawns.GetMap()->GetCreatureBySpawnIdStore().equal_range(guid);
225 for (auto itr = creatureBounds.first; itr != creatureBounds.second;)
226 {
227 Creature* creature = itr->second;
228 ++itr;
229 // For dynamic spawns, save respawn time here
230 if (saveRespawnTime && !creature->GetRespawnCompatibilityMode())
231 creature->SaveRespawnTime();
232 creature->AddObjectToRemoveList();
233 }
234
235 if (alwaysDeleteRespawnTime)
236 spawns.GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, guid, nullptr, true);
237}
238
239// Same on one gameobject
240template<>
241void PoolGroup<GameObject>::Despawn1Object(SpawnedPoolData& spawns, uint64 guid, bool alwaysDeleteRespawnTime, bool saveRespawnTime)
242{
243 auto gameobjectBounds = spawns.GetMap()->GetGameObjectBySpawnIdStore().equal_range(guid);
244 for (auto itr = gameobjectBounds.first; itr != gameobjectBounds.second;)
245 {
246 GameObject* go = itr->second;
247 ++itr;
248
249 // For dynamic spawns, save respawn time here
250 if (saveRespawnTime && !go->GetRespawnCompatibilityMode())
251 go->SaveRespawnTime();
253 }
254
255 if (alwaysDeleteRespawnTime)
256 spawns.GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, guid, nullptr, true);
257}
258
259// Same on one pool
260template<>
261void PoolGroup<Pool>::Despawn1Object(SpawnedPoolData& spawns, uint64 child_pool_id, bool alwaysDeleteRespawnTime, bool /*saveRespawnTime*/)
262{
263 sPoolMgr->DespawnPool(spawns, child_pool_id, alwaysDeleteRespawnTime);
264}
265
266// Method for a pool only to remove any found record causing a circular dependency loop
267template<>
269{
270 for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr)
271 {
272 if (itr->guid == child_pool_id)
273 {
274 ExplicitlyChanced.erase(itr);
275 break;
276 }
277 }
278 for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
279 {
280 if (itr->guid == child_pool_id)
281 {
282 EqualChanced.erase(itr);
283 break;
284 }
285 }
286}
287
288template <class T>
290{
291 int count = limit - spawns.GetSpawnedObjects(poolId);
292
293 // If triggered from some object respawn this object is still marked as spawned
294 // and also counted into m_SpawnedPoolAmount so we need increase count to be
295 // spawned by 1
296 if (triggerFrom)
297 ++count;
298
299 if (count > 0)
300 {
301 PoolObjectList rolledObjects;
302 rolledObjects.reserve(count);
303
304 // roll objects to be spawned
305 if (!ExplicitlyChanced.empty())
306 {
307 float roll = rand_chance();
308
309 for (PoolObject& obj : ExplicitlyChanced)
310 {
311 roll -= obj.chance;
312 // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
313 // so this need explicit check for this case
314 if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsSpawnedObject<T>(obj.guid)))
315 {
316 rolledObjects.push_back(obj);
317 break;
318 }
319 }
320 }
321
322 if (!EqualChanced.empty() && rolledObjects.empty())
323 {
324 std::copy_if(EqualChanced.begin(), EqualChanced.end(), std::back_inserter(rolledObjects), [triggerFrom, &spawns](PoolObject const& object)
325 {
326 return object.guid == triggerFrom || !spawns.IsSpawnedObject<T>(object.guid);
327 });
328
329 Trinity::Containers::RandomResize(rolledObjects, count);
330 }
331
332 // try to spawn rolled objects
333 for (PoolObject& obj : rolledObjects)
334 {
335 if (obj.guid == triggerFrom)
336 {
337 ReSpawn1Object(spawns, &obj);
338 triggerFrom = 0;
339 }
340 else
341 {
342 spawns.AddSpawn<T>(obj.guid, poolId);
343 Spawn1Object(spawns, &obj);
344 }
345 }
346 }
347
348 // One spawn one despawn no count increase
349 if (triggerFrom)
350 DespawnObject(spawns, triggerFrom);
351}
352
353// Method that is actualy doing the spawn job on 1 creature
354template <>
356{
357 if (CreatureData const* data = sObjectMgr->GetCreatureData(obj->guid))
358 {
359 // Spawn if necessary (loaded grids only)
360 // We use spawn coords to spawn
361 if (spawns.GetMap()->IsGridLoaded(data->spawnPoint))
363 }
364}
365
366// Same for 1 gameobject
367template <>
369{
370 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(obj->guid))
371 {
372 // Spawn if necessary (loaded grids only)
373 // We use current coords to unspawn, not spawn coords since creature can have changed grid
374 if (spawns.GetMap()->IsGridLoaded(data->spawnPoint))
375 if (GameObject* go = GameObject::CreateGameObjectFromDB(obj->guid, spawns.GetMap(), false))
376 if (go->isSpawnedByDefault())
377 if (!spawns.GetMap()->AddToMap(go))
378 delete go;
379 }
380}
381
382// Same for 1 pool
383template <>
385{
386 sPoolMgr->SpawnPool(spawns, obj->guid);
387}
388
389// Method that does the respawn job on the specified creature
390template <>
392{
393 Despawn1Object(spawns, obj->guid, false, false);
394 Spawn1Object(spawns, obj);
395}
396
397// Method that does the respawn job on the specified gameobject
398template <>
400{
401 Despawn1Object(spawns, obj->guid, false, false);
402 Spawn1Object(spawns, obj);
403}
404
405// Nothing to do for a child Pool
406template <>
407void PoolGroup<Pool>::ReSpawn1Object(SpawnedPoolData& /*spawns*/, PoolObject* /*obj*/) { }
408
409template <>
411{
412 spawns.GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, guid, nullptr, true);
413}
414
415template <>
417{
418 spawns.GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, guid, nullptr, true);
419}
420
421template <>
423
425// Methods of class PoolMgr
426
427PoolMgr::PoolMgr() = default;
428PoolMgr::~PoolMgr() = default;
429
431{
432 mGameobjectSearchMap.clear();
433 mCreatureSearchMap.clear();
434}
435
437{
438 static PoolMgr instance;
439 return &instance;
440}
441
443{
444 // Pool templates
445 {
446 uint32 oldMSTime = getMSTime();
447
448 QueryResult result = WorldDatabase.Query("SELECT entry, max_limit FROM pool_template");
449 if (!result)
450 {
451 mPoolTemplate.clear();
452 TC_LOG_INFO("server.loading", ">> Loaded 0 object pools. DB table `pool_template` is empty.");
453 return;
454 }
455
456 uint32 count = 0;
457 do
458 {
459 Field* fields = result->Fetch();
460
461 uint32 pool_id = fields[0].GetUInt32();
462
463 PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id];
464 pPoolTemplate.MaxLimit = fields[1].GetUInt32();
465 pPoolTemplate.MapId = -1;
466
467 ++count;
468 }
469 while (result->NextRow());
470
471 TC_LOG_INFO("server.loading", ">> Loaded {} objects pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
472 }
473
474 // Creatures
475
476 TC_LOG_INFO("server.loading", "Loading Creatures Pooling Data...");
477 {
478 uint32 oldMSTime = getMSTime();
479
480 // 1 2 3
481 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 0");
482
483 if (!result)
484 {
485 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in pools. DB table `pool_creature` is empty.");
486 }
487 else
488 {
489 uint32 count = 0;
490 do
491 {
492 Field* fields = result->Fetch();
493
494 uint64 guid = fields[0].GetUInt64();
495 uint32 pool_id = fields[1].GetUInt32();
496 float chance = fields[2].GetFloat();
497
498 CreatureData const* data = sObjectMgr->GetCreatureData(guid);
499 if (!data)
500 {
501 TC_LOG_ERROR("sql.sql", "`pool_creature` has a non existing creature spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
502 continue;
503 }
504 auto it = mPoolTemplate.find(pool_id);
505 if (it == mPoolTemplate.end())
506 {
507 TC_LOG_ERROR("sql.sql", "`pool_creature` pool id ({}) is not in `pool_template`, skipped.", pool_id);
508 continue;
509 }
510 if (chance < 0 || chance > 100)
511 {
512 TC_LOG_ERROR("sql.sql", "`pool_creature` has an invalid chance ({}) for creature guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
513 continue;
514 }
515 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
516 if (pPoolTemplate->MapId == -1)
517 pPoolTemplate->MapId = int32(data->mapId);
518
519 if (pPoolTemplate->MapId != int32(data->mapId))
520 {
521 TC_LOG_ERROR("sql.sql", "`pool_creature` has creature spawns on multiple different maps for creature guid ({}) in pool id ({}), skipped.", guid, pool_id);
522 continue;
523 }
524
525 PoolObject plObject = PoolObject(guid, chance);
526 PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id];
527 cregroup.SetPoolId(pool_id);
528 cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
529 SearchPair p(guid, pool_id);
530 mCreatureSearchMap.insert(p);
531
532 ++count;
533 }
534 while (result->NextRow());
535
536 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
537 }
538 }
539
540 // Gameobjects
541
542 TC_LOG_INFO("server.loading", "Loading Gameobject Pooling Data...");
543 {
544 uint32 oldMSTime = getMSTime();
545
546 // 1 2 3
547 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 1");
548
549 if (!result)
550 {
551 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty.");
552 }
553 else
554 {
555 uint32 count = 0;
556 do
557 {
558 Field* fields = result->Fetch();
559
560 uint64 guid = fields[0].GetUInt64();
561 uint32 pool_id = fields[1].GetUInt32();
562 float chance = fields[2].GetFloat();
563
564 GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
565 if (!data)
566 {
567 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
568 continue;
569 }
570
571 auto it = mPoolTemplate.find(pool_id);
572 if (it == mPoolTemplate.end())
573 {
574 TC_LOG_ERROR("sql.sql", "`pool_gameobject` pool id ({}) is not in `pool_template`, skipped.", pool_id);
575 continue;
576 }
577
578 if (chance < 0 || chance > 100)
579 {
580 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has an invalid chance ({}) for gameobject guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
581 continue;
582 }
583
584 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
585 if (pPoolTemplate->MapId == -1)
586 pPoolTemplate->MapId = int32(data->mapId);
587
588 if (pPoolTemplate->MapId != int32(data->mapId))
589 {
590 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has gameobject spawns on multiple different maps for gameobject guid ({}) in pool id ({}), skipped.", guid, pool_id);
591 continue;
592 }
593
594 PoolObject plObject = PoolObject(guid, chance);
596 gogroup.SetPoolId(pool_id);
597 gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
598 SearchPair p(guid, pool_id);
599 mGameobjectSearchMap.insert(p);
600
601 ++count;
602 }
603 while (result->NextRow());
604
605 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject in pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
606 }
607 }
608
609 // Pool of pools
610
611 TC_LOG_INFO("server.loading", "Loading Mother Pooling Data...");
612 {
613 uint32 oldMSTime = getMSTime();
614
615 // 1 2 3
616 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 2");
617
618 if (!result)
619 {
620 TC_LOG_INFO("server.loading", ">> Loaded 0 pools in pools");
621 }
622 else
623 {
624 uint32 count = 0;
625 do
626 {
627 Field* fields = result->Fetch();
628
629 uint32 child_pool_id = fields[0].GetUInt64();
630 uint32 mother_pool_id = fields[1].GetUInt32();
631 float chance = fields[2].GetFloat();
632
633 {
634 auto it = mPoolTemplate.find(mother_pool_id);
635 if (it == mPoolTemplate.end())
636 {
637 TC_LOG_ERROR("sql.sql", "`pool_pool` mother_pool id ({}) is not in `pool_template`, skipped.", mother_pool_id);
638 continue;
639 }
640 }
641 {
642 auto it = mPoolTemplate.find(child_pool_id);
643 if (it == mPoolTemplate.end())
644 {
645 TC_LOG_ERROR("sql.sql", "`pool_pool` included pool_id ({}) is not in `pool_template`, skipped.", child_pool_id);
646 continue;
647 }
648 }
649 if (mother_pool_id == child_pool_id)
650 {
651 TC_LOG_ERROR("sql.sql", "`pool_pool` pool_id ({}) includes itself, dead-lock detected, skipped.", child_pool_id);
652 continue;
653 }
654 if (chance < 0 || chance > 100)
655 {
656 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);
657 continue;
658 }
659 PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id];
660 PoolObject plObject = PoolObject(child_pool_id, chance);
661 PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id];
662 plgroup.SetPoolId(mother_pool_id);
663 plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit);
664 SearchPair p(child_pool_id, mother_pool_id);
665 mPoolSearchMap.insert(p);
666
667 ++count;
668 }
669 while (result->NextRow());
670
671 // Now check for circular reference
672 // All pool_ids are in pool_template
673 for (auto const& it : mPoolTemplate)
674 {
675 std::set<uint32> checkedPools;
676 for (SearchMap::iterator poolItr = mPoolSearchMap.find(it.first); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second))
677 {
678 if (mPoolTemplate[poolItr->first].MapId != -1)
679 {
680 if (mPoolTemplate[poolItr->second].MapId == -1)
681 mPoolTemplate[poolItr->second].MapId = mPoolTemplate[poolItr->first].MapId;
682
683 if (mPoolTemplate[poolItr->second].MapId != mPoolTemplate[poolItr->first].MapId)
684 {
685 TC_LOG_ERROR("sql.sql", "`pool_pool` has child pools on multiple maps in pool id ({}), skipped.", poolItr->second);
686 mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first);
687 mPoolSearchMap.erase(poolItr);
688 --count;
689 break;
690 }
691 }
692
693 checkedPools.insert(poolItr->first);
694 if (checkedPools.find(poolItr->second) != checkedPools.end())
695 {
696 std::ostringstream ss;
697 ss << "The pool(s) ";
698 for (std::set<uint32>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr)
699 ss << *itr << ' ';
700 ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool "
701 << poolItr->first << " and child pool " << poolItr->second;
702 TC_LOG_ERROR("sql.sql", "{}", ss.str());
703 mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first);
704 mPoolSearchMap.erase(poolItr);
705 --count;
706 break;
707 }
708 }
709 }
710
711 TC_LOG_INFO("server.loading", ">> Loaded {} pools in mother pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
712 }
713 }
714
715 for (auto const& [poolId, templateData] : mPoolTemplate)
716 {
717 if (IsEmpty(poolId))
718 {
719 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);
720 continue;
721 }
722 ASSERT(templateData.MapId != -1);
723 }
724
725 // 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
726 TC_LOG_INFO("server.loading", "Starting objects pooling system...");
727 {
728 uint32 oldMSTime = getMSTime();
729
730 QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_members.spawnId, pool_members.poolSpawnId FROM pool_template"
731 " LEFT JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry"
732 " LEFT JOIN pool_members ON pool_members.type = 2 AND pool_template.entry = pool_members.spawnId WHERE game_event_pool.pool_entry IS NULL");
733
734 if (!result)
735 {
736 TC_LOG_INFO("server.loading", ">> Pool handling system initialized, 0 pools spawned.");
737 }
738 else
739 {
740 uint32 count = 0;
741 do
742 {
743 Field* fields = result->Fetch();
744 uint32 pool_entry = fields[0].GetUInt32();
745 uint32 pool_pool_id = fields[1].GetUInt64();
746
747 if (IsEmpty(pool_entry))
748 continue;
749
750 if (!CheckPool(pool_entry))
751 {
752 if (pool_pool_id)
753 // The pool is a child pool in pool_pool table. Ideally we should remove it from the pool handler to ensure it never gets spawned,
754 // however that could recursively invalidate entire chain of mother pools. It can be done in the future but for now we'll do nothing.
755 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());
756 else
757 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);
758 continue;
759 }
760
761 // Don't spawn child pools, they are spawned recursively by their parent pools
762 if (!pool_pool_id)
763 {
764 mAutoSpawnPoolsPerMap[mPoolTemplate[pool_entry].MapId].push_back(pool_entry);
765 count++;
766 }
767 }
768 while (result->NextRow());
769
770 TC_LOG_DEBUG("pool", "Pool handling system initialized, {} pools will be spawned by default in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
771
772 }
773 }
774}
775
776// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
777// If it's same, the creature is respawned only (added back to map)
778template<>
779void PoolMgr::SpawnPool<Creature>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid)
780{
781 auto it = mPoolCreatureGroups.find(pool_id);
782 if (it != mPoolCreatureGroups.end() && !it->second.isEmpty())
783 it->second.SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, db_guid);
784}
785
786// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
787// If it's same, the gameobject is respawned only (added back to map)
788template<>
789void PoolMgr::SpawnPool<GameObject>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid)
790{
791 auto it = mPoolGameobjectGroups.find(pool_id);
792 if (it != mPoolGameobjectGroups.end() && !it->second.isEmpty())
793 it->second.SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, db_guid);
794}
795
796// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
797// If it's same, the pool is respawned only
798template<>
799void PoolMgr::SpawnPool<Pool>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 sub_pool_id)
800{
801 auto it = mPoolPoolGroups.find(pool_id);
802 if (it != mPoolPoolGroups.end() && !it->second.isEmpty())
803 it->second.SpawnObject(spawnedPoolData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
804}
805
806void PoolMgr::SpawnPool(SpawnedPoolData& spawnedPoolData, uint32 pool_id)
807{
808 SpawnPool<Pool>(spawnedPoolData, pool_id, 0);
809 SpawnPool<GameObject>(spawnedPoolData, pool_id, 0);
810 SpawnPool<Creature>(spawnedPoolData, pool_id, 0);
811}
812
813// Call to despawn a pool, all gameobjects/creatures in this pool are removed
814void PoolMgr::DespawnPool(SpawnedPoolData& spawnedPoolData, uint32 pool_id, bool alwaysDeleteRespawnTime)
815{
816 {
817 auto it = mPoolCreatureGroups.find(pool_id);
818 if (it != mPoolCreatureGroups.end() && !it->second.isEmpty())
819 it->second.DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime);
820 }
821 {
822 auto it = mPoolGameobjectGroups.find(pool_id);
823 if (it != mPoolGameobjectGroups.end() && !it->second.isEmpty())
824 it->second.DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime);
825 }
826 {
827 auto it = mPoolPoolGroups.find(pool_id);
828 if (it != mPoolPoolGroups.end() && !it->second.isEmpty())
829 it->second.DespawnObject(spawnedPoolData, 0, alwaysDeleteRespawnTime);
830 }
831}
832
833// Selects proper template overload to call based on passed type
835{
836 switch (type)
837 {
839 return IsPartOfAPool<Creature>(spawnId);
841 return IsPartOfAPool<GameObject>(spawnId);
843 return 0;
844 default:
845 ABORT_MSG("Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId " UI64FMTD ")", uint32(type), spawnId);
846 return 0;
847 }
848}
849
850bool PoolMgr::IsEmpty(uint32 pool_id) const
851{
852 {
853 auto it = mPoolGameobjectGroups.find(pool_id);
854 if (it != mPoolGameobjectGroups.end() && !it->second.isEmptyDeepCheck())
855 return false;
856 }
857 {
858 auto it = mPoolCreatureGroups.find(pool_id);
859 if (it != mPoolCreatureGroups.end() && !it->second.isEmptyDeepCheck())
860 return false;
861 }
862 {
863 auto it = mPoolPoolGroups.find(pool_id);
864 if (it != mPoolPoolGroups.end() && !it->second.isEmptyDeepCheck())
865 return false;
866 }
867 return true;
868}
869
870// Method that check chance integrity of the creatures and gameobjects in this pool
871bool PoolMgr::CheckPool(uint32 pool_id) const
872{
873 {
874 auto it = mPoolGameobjectGroups.find(pool_id);
875 if (it != mPoolGameobjectGroups.end() && !it->second.CheckPool())
876 return false;
877 }
878 {
879 auto it = mPoolCreatureGroups.find(pool_id);
880 if (it != mPoolCreatureGroups.end() && !it->second.CheckPool())
881 return false;
882 }
883 {
884 auto it = mPoolPoolGroups.find(pool_id);
885 if (it != mPoolPoolGroups.end() && !it->second.CheckPool())
886 return false;
887 }
888 return true;
889}
890
891// Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
892// Here we cache only the creature/gameobject whose guid is passed as parameter
893// Then the spawn pool call will use this cache to decide
894template<typename T>
895void PoolMgr::UpdatePool(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id)
896{
897 if (uint32 motherpoolid = IsPartOfAPool<Pool>(pool_id))
898 SpawnPool<Pool>(spawnedPoolData, motherpoolid, pool_id);
899 else
900 SpawnPool<T>(spawnedPoolData, pool_id, db_guid_or_pool_id);
901}
902
903template void PoolMgr::UpdatePool<Pool>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id);
904template void PoolMgr::UpdatePool<GameObject>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id);
905template void PoolMgr::UpdatePool<Creature>(SpawnedPoolData& spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id);
906
907void PoolMgr::UpdatePool(SpawnedPoolData& spawnedPoolData, uint32 pool_id, SpawnObjectType type, uint64 spawnId)
908{
909 switch (type)
910 {
912 UpdatePool<Creature>(spawnedPoolData, pool_id, spawnId);
913 break;
915 UpdatePool<GameObject>(spawnedPoolData, pool_id, spawnId);
916 break;
917 default:
918 ABORT_MSG("Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId " UI64FMTD ")", uint32(type), spawnId);
919 }
920}
921
922std::unique_ptr<SpawnedPoolData> PoolMgr::InitPoolsForMap(Map* map)
923{
924 std::unique_ptr<SpawnedPoolData> spawnedPoolData = std::make_unique<SpawnedPoolData>(map);
925 if (std::vector<uint32> const* poolIds = Trinity::Containers::MapGetValuePtr(mAutoSpawnPoolsPerMap, spawnedPoolData->GetMap()->GetId()))
926 for (uint32 poolId : *poolIds)
927 SpawnPool(*spawnedPoolData, poolId);
928
929 return spawnedPoolData;
930}
931
933{
935}
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
#define UI64FMTD
Definition: Define.h:126
#define TC_GAME_API
Definition: Define.h:123
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define ABORT_MSG
Definition: Errors.h:75
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_DEBUG(filterType__,...)
Definition: Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define sObjectMgr
Definition: ObjectMgr.h:1946
#define sPoolMgr
Definition: PoolMgr.h:179
float rand_chance()
Definition: Random.cpp:81
SpawnObjectType
Definition: SpawnData.h:33
@ SPAWN_TYPE_GAMEOBJECT
Definition: SpawnData.h:35
@ SPAWN_TYPE_AREATRIGGER
Definition: SpawnData.h:36
@ SPAWN_TYPE_CREATURE
Definition: SpawnData.h:34
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
static Creature * CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true, bool allowDuplicate=false)
Definition: Creature.cpp:1199
bool GetRespawnCompatibilityMode() const
Definition: Creature.h:408
void SaveRespawnTime(uint32 forceDelay=0)
Definition: Creature.cpp:2666
Class used to access individual fields of database query result.
Definition: Field.h:90
uint64 GetUInt64() const
Definition: Field.cpp:78
float GetFloat() const
Definition: Field.cpp:94
uint32 GetUInt32() const
Definition: Field.cpp:62
bool isSpawnedByDefault() const
Definition: GameObject.h:262
static GameObject * CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true)
void SaveRespawnTime(uint32 forceDelay=0)
bool GetRespawnCompatibilityMode()
Definition: GameObject.h:374
Definition: Map.h:189
bool AddToMap(T *)
Definition: Map.cpp:550
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr, bool alwaysDeleteFromDB=false)
Definition: Map.h:687
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition: Map.h:429
bool IsGridLoaded(uint32 gridId) const
Definition: Map.h:239
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition: Map.h:425
uint64 LowType
Definition: ObjectGuid.h:278
void SetPoolId(uint32 pool_id)
Definition: PoolMgr.h:99
void ReSpawn1Object(SpawnedPoolData &spawns, PoolObject *obj)
void DespawnObject(SpawnedPoolData &spawns, uint64 guid=0, bool alwaysDeleteRespawnTime=false)
Definition: PoolMgr.cpp:187
void Despawn1Object(SpawnedPoolData &spawns, uint64 guid, bool alwaysDeleteRespawnTime=false, bool saveRespawnTime=true)
void RemoveOneRelation(uint32 child_pool_id)
void Spawn1Object(SpawnedPoolData &spawns, PoolObject *obj)
PoolObjectList ExplicitlyChanced
Definition: PoolMgr.h:115
void AddEntry(PoolObject &poolitem, uint32 maxentries)
Definition: PoolMgr.cpp:160
bool isEmptyDeepCheck() const
Definition: PoolMgr.cpp:139
bool CheckPool() const
Definition: PoolMgr.cpp:170
void RemoveRespawnTimeFromDB(SpawnedPoolData &spawns, uint64 guid)
std::vector< PoolObject > PoolObjectList
Definition: PoolMgr.h:89
PoolObjectList EqualChanced
Definition: PoolMgr.h:116
void SpawnObject(SpawnedPoolData &spawns, uint32 limit, uint64 triggerFrom)
Definition: PoolMgr.cpp:289
std::pair< uint64, uint32 > SearchPair
Definition: PoolMgr.h:166
bool IsEmpty(uint32 pool_id) const
Definition: PoolMgr.cpp:850
bool CheckPool(uint32 pool_id) const
Definition: PoolMgr.cpp:871
std::unique_ptr< SpawnedPoolData > InitPoolsForMap(Map *map)
Definition: PoolMgr.cpp:922
void Initialize()
Definition: PoolMgr.cpp:430
void UpdatePool(SpawnedPoolData &spawnedPoolData, uint32 pool_id, uint64 db_guid_or_pool_id)
Definition: PoolMgr.cpp:895
static PoolMgr * instance()
Definition: PoolMgr.cpp:436
void SpawnPool(SpawnedPoolData &spawnedPoolData, uint32 pool_id)
Definition: PoolMgr.cpp:806
PoolTemplateData const * GetPoolTemplate(uint16 pool_id) const
Definition: PoolMgr.cpp:932
PoolGroupCreatureMap mPoolCreatureGroups
Definition: PoolMgr.h:170
void LoadFromDB()
Definition: PoolMgr.cpp:442
PoolGroupPoolMap mPoolPoolGroups
Definition: PoolMgr.h:172
SearchMap mGameobjectSearchMap
Definition: PoolMgr.h:174
PoolTemplateDataMap mPoolTemplate
Definition: PoolMgr.h:169
std::unordered_map< uint32, std::vector< uint32 > > mAutoSpawnPoolsPerMap
Definition: PoolMgr.h:176
uint32 IsPartOfAPool(uint64 db_guid_or_pool_id) const
SearchMap mPoolSearchMap
Definition: PoolMgr.h:175
PoolGroupGameObjectMap mPoolGameobjectGroups
Definition: PoolMgr.h:171
void DespawnPool(SpawnedPoolData &spawnedPoolData, uint32 pool_id, bool alwaysDeleteRespawnTime=false)
Definition: PoolMgr.cpp:814
SearchMap mCreatureSearchMap
Definition: PoolMgr.h:173
SpawnedPoolObjects mSpawnedCreatures
Definition: PoolMgr.h:81
SpawnedPoolObjects mSpawnedGameobjects
Definition: PoolMgr.h:82
Map * GetMap() const
Definition: PoolMgr.h:65
SpawnedPoolData(Map *owner)
Definition: PoolMgr.cpp:35
void AddSpawn(uint64 db_guid_or_pool_id, uint32 pool_id)
uint32 GetSpawnedObjects(uint32 pool_id) const
Definition: PoolMgr.cpp:39
bool IsSpawnedObject(uint64 db_guid_or_pool_id) const
void RemoveSpawn(uint64 db_guid_or_pool_id, uint32 pool_id)
SpawnedPoolPools mSpawnedPools
Definition: PoolMgr.h:83
void AddObjectToRemoveList()
Definition: Object.cpp:1824
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
void RandomResize(C &container, std::size_t requestedSize)
Definition: Containers.h:67
STL namespace.
uint64 guid
Definition: PoolMgr.h:42
float chance
Definition: PoolMgr.h:43
PoolObject(uint64 _guid, float _chance)
Definition: PoolMgr.cpp:28
uint32 MaxLimit
Definition: PoolMgr.h:36
uint32 mapId
Definition: SpawnData.h:94