TrinityCore
Loading...
Searching...
No Matches
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 "DatabaseEnv.h"
21#include "Log.h"
22#include "MapManager.h"
23#include "ObjectMgr.h"
24
26// template class ActivePoolData
27
28// Method that tell amount spawned objects/subpools
30{
31 ActivePoolPools::const_iterator itr = mSpawnedPools.find(pool_id);
32 return itr != mSpawnedPools.end() ? itr->second : 0;
33}
34
35// Method that tell if a creature is spawned currently
36template<>
37TC_GAME_API bool ActivePoolData::IsActiveObject<Creature>(uint32 db_guid) const
38{
39 return mSpawnedCreatures.find(db_guid) != mSpawnedCreatures.end();
40}
41
42// Method that tell if a gameobject is spawned currently
43template<>
44TC_GAME_API bool ActivePoolData::IsActiveObject<GameObject>(uint32 db_guid) const
45{
46 return mSpawnedGameobjects.find(db_guid) != mSpawnedGameobjects.end();
47}
48
49// Method that tell if a pool is spawned currently
50template<>
51TC_GAME_API bool ActivePoolData::IsActiveObject<Pool>(uint32 sub_pool_id) const
52{
53 return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end();
54}
55
56template<>
57void ActivePoolData::ActivateObject<Creature>(uint32 db_guid, uint32 pool_id)
58{
59 mSpawnedCreatures.insert(db_guid);
60 ++mSpawnedPools[pool_id];
61}
62
63template<>
64void ActivePoolData::ActivateObject<GameObject>(uint32 db_guid, uint32 pool_id)
65{
66 mSpawnedGameobjects.insert(db_guid);
67 ++mSpawnedPools[pool_id];
68}
69
70template<>
71void ActivePoolData::ActivateObject<Pool>(uint32 sub_pool_id, uint32 pool_id)
72{
73 mSpawnedPools[sub_pool_id] = 0;
74 ++mSpawnedPools[pool_id];
77template<>
78void ActivePoolData::RemoveObject<Creature>(uint32 db_guid, uint32 pool_id)
79{
80 mSpawnedCreatures.erase(db_guid);
81 uint32& val = mSpawnedPools[pool_id];
82 if (val > 0)
83 --val;
84}
85
86template<>
87void ActivePoolData::RemoveObject<GameObject>(uint32 db_guid, uint32 pool_id)
88{
89 mSpawnedGameobjects.erase(db_guid);
90 uint32& val = mSpawnedPools[pool_id];
91 if (val > 0)
92 --val;
93}
94
95template<>
96void ActivePoolData::RemoveObject<Pool>(uint32 sub_pool_id, uint32 pool_id)
97{
98 mSpawnedPools.erase(sub_pool_id);
99 uint32& val = mSpawnedPools[pool_id];
100 if (val > 0)
101 --val;
102}
103
105// Methods of template class PoolGroup
106
107// Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
108template <class T>
109void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
110{
111 if (poolitem.chance != 0 && maxentries == 1)
112 ExplicitlyChanced.push_back(poolitem);
113 else
114 EqualChanced.push_back(poolitem);
115}
116
117// Method to check the chances are proper in this object pool
118template <class T>
120{
121 if (EqualChanced.empty())
122 {
123 float chance = 0;
124 for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
125 chance += ExplicitlyChanced[i].chance;
126 if (chance != 100 && chance != 0)
127 return false;
128 }
129 return true;
130}
131
132// Main method to despawn a creature or gameobject in a pool
133template<class T>
134void PoolGroup<T>::DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime)
135{
136 // if spawned
137 if (spawns.IsActiveObject<T>(guid))
138 {
139 Despawn1Object(guid, alwaysDeleteRespawnTime);
140 spawns.RemoveObject<T>(guid, poolId);
141 }
142 else if (alwaysDeleteRespawnTime)
143 RemoveRespawnTimeFromDB(guid);
144}
145
146template <class T>
147void PoolGroup<T>::DespawnAllObjects(ActivePoolData& spawns, bool alwaysDeleteRespawnTime)
148{
149 for (PoolObject& pooledObject : EqualChanced)
150 DespawnObject(spawns, pooledObject.guid, alwaysDeleteRespawnTime);
151
152 for (PoolObject& pooledObject : ExplicitlyChanced)
153 DespawnObject(spawns, pooledObject.guid, alwaysDeleteRespawnTime);
154}
155
156// Method that is actualy doing the removal job on one creature
157template<>
158void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime, bool saveRespawnTime)
159{
160 if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
161 {
162 sObjectMgr->RemoveCreatureFromGrid(guid, data);
163
164 Map* map = sMapMgr->CreateBaseMap(data->mapId);
165 if (!map->Instanceable())
166 {
167 auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid);
168 for (auto itr = creatureBounds.first; itr != creatureBounds.second;)
169 {
170 Creature* creature = itr->second;
171 ++itr;
172 // For dynamic spawns, save respawn time here
173 if (saveRespawnTime && !creature->GetRespawnCompatibilityMode())
174 creature->SaveRespawnTime();
175 creature->AddObjectToRemoveList();
176 }
177
178 if (alwaysDeleteRespawnTime)
180 }
181 }
182}
183
184// Same on one gameobject
185template<>
186void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime, bool saveRespawnTime)
187{
188 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid))
189 {
190 sObjectMgr->RemoveGameobjectFromGrid(guid, data);
191
192 Map* map = sMapMgr->CreateBaseMap(data->mapId);
193 if (!map->Instanceable())
194 {
195 auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid);
196 for (auto itr = gameobjectBounds.first; itr != gameobjectBounds.second;)
197 {
198 GameObject* go = itr->second;
199 ++itr;
200
201 // For dynamic spawns, save respawn time here
202 if (saveRespawnTime && !go->GetRespawnCompatibilityMode())
203 go->SaveRespawnTime();
205 }
206
207 if (alwaysDeleteRespawnTime)
209 }
210 }
211}
212
213// Same on one pool
214template<>
215void PoolGroup<Pool>::Despawn1Object(uint32 child_pool_id, bool alwaysDeleteRespawnTime, bool /*saveRespawnTime*/)
216{
217 sPoolMgr->DespawnPool(child_pool_id, alwaysDeleteRespawnTime);
218}
219
220// Method for a pool only to remove any found record causing a circular dependency loop
221template<>
223{
224 for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr)
225 {
226 if (itr->guid == child_pool_id)
227 {
228 ExplicitlyChanced.erase(itr);
229 break;
230 }
231 }
232 for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
233 {
234 if (itr->guid == child_pool_id)
235 {
236 EqualChanced.erase(itr);
237 break;
238 }
239 }
240}
241
242template <class T>
243void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom)
244{
245 // First clear the object that triggered the respawn, if any.
246 // DespawnObject is responsible for decrementing the active object counter.
247 if (triggerFrom)
248 DespawnObject(spawns, triggerFrom);
249
250 int32 count = limit - spawns.GetActiveObjectCount(poolId);
251 if (count <= 0)
252 return;
253
254 PoolObjectList candidates;
255 candidates.reserve(EqualChanced.size() + ExplicitlyChanced.size());
256
257 // Add all not already active candidates.
258 for (PoolObject& obj : EqualChanced)
259 if (!spawns.IsActiveObject<T>(obj.guid))
260 candidates.push_back(obj);
261
262 for (PoolObject& obj : ExplicitlyChanced)
263 if (!spawns.IsActiveObject<T>(obj.guid))
264 candidates.push_back(obj);
265
266 if (candidates.empty())
267 return;
268
269 PoolObjectList rolledObjects;
270 rolledObjects.reserve(count);
271
272 // Attempt to select one object based on explicit chance.
273 if (!ExplicitlyChanced.empty())
274 {
275 float roll = (float)rand_chance();
276 for (PoolObject& candidate : candidates)
277 {
278 if (candidate.chance > 0)
279 {
280 roll -= candidate.chance;
281 if (roll < 0)
282 {
283 rolledObjects.push_back(candidate);
284 std::swap(candidate, candidates.back());
285 candidates.pop_back();
286 break; // We only roll for one chanced object.
287 }
288 }
289 }
290 }
291
292 // Fill the remaining slots with random selections from the rest of the candidates.
293 uint32 remainingCount = count - rolledObjects.size();
294 if (remainingCount > 0 && !candidates.empty())
295 {
296 if (candidates.size() > remainingCount)
297 Trinity::Containers::RandomResize(candidates, remainingCount);
298
299 rolledObjects.insert(rolledObjects.end(), candidates.begin(), candidates.end());
300 }
301
302 // Spawn all the objects we've selected.
303 for (PoolObject& objToSpawn : rolledObjects)
304 {
305 spawns.ActivateObject<T>(objToSpawn.guid, poolId);
306 Spawn1Object(&objToSpawn);
307 }
308}
309
310// Method that is actualy doing the spawn job on 1 creature
311template <>
313{
314 if (CreatureData const* data = sObjectMgr->GetCreatureData(obj->guid))
315 {
316 sObjectMgr->AddCreatureToGrid(obj->guid, data);
317
318 // Spawn if necessary (loaded grids only)
319 Map* map = sMapMgr->CreateBaseMap(data->mapId);
320 // We use spawn coords to spawn
321 if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
322 {
323 Creature* creature = new Creature();
324 //TC_LOG_DEBUG("pool", "Spawning creature {}", guid);
325 if (!creature->LoadFromDB(obj->guid, map, true, false))
326 {
327 delete creature;
328 return;
329 }
330 }
331 }
332}
333
334// Same for 1 gameobject
335template <>
337{
338 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(obj->guid))
339 {
340 sObjectMgr->AddGameobjectToGrid(obj->guid, data);
341 // Spawn if necessary (loaded grids only)
342 // this base map checked as non-instanced and then only existed
343 Map* map = sMapMgr->CreateBaseMap(data->mapId);
344 // We use current coords to unspawn, not spawn coords since creature can have changed grid
345 if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint))
346 {
347 GameObject* pGameobject = new GameObject;
348 //TC_LOG_DEBUG("pool", "Spawning gameobject {}", guid);
349 if (!pGameobject->LoadFromDB(obj->guid, map, false))
350 {
351 delete pGameobject;
352 return;
353 }
354 else
355 {
356 if (pGameobject->isSpawnedByDefault())
357 map->AddToMap(pGameobject);
358 }
359 }
360 }
361}
362
363// Same for 1 pool
364template <>
366{
367 sPoolMgr->SpawnPool(obj->guid);
368}
369
370template <>
372{
373 if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
374 {
375 Map* map = sMapMgr->CreateBaseMap(data->mapId);
376 if (!map->Instanceable())
377 {
378 map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, guid, nullptr, true);
379 }
380 }
381}
382
383template <>
385{
386 if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid))
387 {
388 Map* map = sMapMgr->CreateBaseMap(data->mapId);
389 if (!map->Instanceable())
390 {
391 map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, guid, nullptr, true);
392 }
393 }
394}
395
396template <>
398
400// Methods of class PoolMgr
401
403
405{
406 mGameobjectSearchMap.clear();
407 mCreatureSearchMap.clear();
408}
409
411{
412 static PoolMgr instance;
413 return &instance;
414}
415
417{
418 // Pool templates
419 {
420 uint32 oldMSTime = getMSTime();
421
422 QueryResult result = WorldDatabase.Query("SELECT entry, max_limit FROM pool_template");
423 if (!result)
424 {
425 mPoolTemplate.clear();
426 TC_LOG_INFO("server.loading", ">> Loaded 0 object pools. DB table `pool_template` is empty.");
427 return;
428 }
429
430 uint32 count = 0;
431 do
432 {
433 Field* fields = result->Fetch();
434
435 uint32 pool_id = fields[0].GetUInt32();
436
437 PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id];
438 pPoolTemplate.MaxLimit = fields[1].GetUInt32();
439
440 ++count;
441 }
442 while (result->NextRow());
443
444 TC_LOG_INFO("server.loading", ">> Loaded {} objects pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
445 }
446
447 // Creatures
448
449 TC_LOG_INFO("server.loading", "Loading Creatures Pooling Data...");
450 {
451 uint32 oldMSTime = getMSTime();
452
453 // 1 2 3
454 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 0");
455
456 if (!result)
457 {
458 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in pools. DB table `pool_creature` is empty.");
459 }
460 else
461 {
462 uint32 count = 0;
463 do
464 {
465 Field* fields = result->Fetch();
466
467 ObjectGuid::LowType guid = fields[0].GetUInt32();
468 uint32 pool_id = fields[1].GetUInt32();
469 float chance = fields[2].GetFloat();
470
471 CreatureData const* data = sObjectMgr->GetCreatureData(guid);
472 if (!data)
473 {
474 TC_LOG_ERROR("sql.sql", "`pool_creature` has a non existing creature spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
475 continue;
476 }
477 auto it = mPoolTemplate.find(pool_id);
478 if (it == mPoolTemplate.end())
479 {
480 TC_LOG_ERROR("sql.sql", "`pool_creature` pool id ({}) is not in `pool_template`, skipped.", pool_id);
481 continue;
482 }
483 if (chance < 0 || chance > 100)
484 {
485 TC_LOG_ERROR("sql.sql", "`pool_creature` has an invalid chance ({}) for creature guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
486 continue;
487 }
488 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
489 PoolObject plObject = PoolObject(guid, chance);
490 PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id];
491 cregroup.SetPoolId(pool_id);
492 cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
493 SearchPair p(guid, pool_id);
494 mCreatureSearchMap.insert(p);
495
496 ++count;
497 }
498 while (result->NextRow());
499
500 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
501 }
502 }
503
504 // Gameobjects
505
506 TC_LOG_INFO("server.loading", "Loading Gameobject Pooling Data...");
507 {
508 uint32 oldMSTime = getMSTime();
509
510 // 1 2 3
511 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 1");
512
513 if (!result)
514 {
515 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty.");
516 }
517 else
518 {
519 uint32 count = 0;
520 do
521 {
522 Field* fields = result->Fetch();
523
524 ObjectGuid::LowType guid = fields[0].GetUInt32();
525 uint32 pool_id = fields[1].GetUInt32();
526 float chance = fields[2].GetFloat();
527
528 GameObjectData const* data = sObjectMgr->GetGameObjectData(guid);
529 if (!data)
530 {
531 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: {}) defined for pool id ({}), skipped.", guid, pool_id);
532 continue;
533 }
534
535 GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(data->id);
536 ASSERT(goinfo);
537 if (goinfo->type != GAMEOBJECT_TYPE_CHEST &&
538 goinfo->type != GAMEOBJECT_TYPE_GOOBER &&
540 {
541 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a not lootable gameobject spawn (GUID: {}, type: {}) defined for pool id ({}), skipped.", guid, goinfo->type, pool_id);
542 continue;
543 }
544
545 auto it = mPoolTemplate.find(pool_id);
546 if (it == mPoolTemplate.end())
547 {
548 TC_LOG_ERROR("sql.sql", "`pool_gameobject` pool id ({}) is not in `pool_template`, skipped.", pool_id);
549 continue;
550 }
551
552 if (chance < 0 || chance > 100)
553 {
554 TC_LOG_ERROR("sql.sql", "`pool_gameobject` has an invalid chance ({}) for gameobject guid ({}) in pool id ({}), skipped.", chance, guid, pool_id);
555 continue;
556 }
557
558 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
559 PoolObject plObject = PoolObject(guid, chance);
561 gogroup.SetPoolId(pool_id);
562 gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
563 SearchPair p(guid, pool_id);
564 mGameobjectSearchMap.insert(p);
565
566 ++count;
567 }
568 while (result->NextRow());
569
570 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject in pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
571 }
572 }
573
574 // Pool of pools
575
576 TC_LOG_INFO("server.loading", "Loading Mother Pooling Data...");
577 {
578 uint32 oldMSTime = getMSTime();
579
580 // 1 2 3
581 QueryResult result = WorldDatabase.Query("SELECT spawnId, poolSpawnId, chance FROM pool_members WHERE type = 2");
582
583 if (!result)
584 {
585 TC_LOG_INFO("server.loading", ">> Loaded 0 pools in pools");
586 }
587 else
588 {
589 uint32 count = 0;
590 do
591 {
592 Field* fields = result->Fetch();
593
594 uint32 child_pool_id = fields[0].GetUInt32();
595 uint32 mother_pool_id = fields[1].GetUInt32();
596 float chance = fields[2].GetFloat();
597
598 {
599 auto it = mPoolTemplate.find(mother_pool_id);
600 if (it == mPoolTemplate.end())
601 {
602 TC_LOG_ERROR("sql.sql", "`pool_pool` mother_pool id ({}) is not in `pool_template`, skipped.", mother_pool_id);
603 continue;
604 }
605 }
606 {
607 auto it = mPoolTemplate.find(child_pool_id);
608 if (it == mPoolTemplate.end())
609 {
610 TC_LOG_ERROR("sql.sql", "`pool_pool` included pool_id ({}) is not in `pool_template`, skipped.", child_pool_id);
611 continue;
612 }
613 }
614 if (mother_pool_id == child_pool_id)
615 {
616 TC_LOG_ERROR("sql.sql", "`pool_pool` pool_id ({}) includes itself, dead-lock detected, skipped.", child_pool_id);
617 continue;
618 }
619 if (chance < 0 || chance > 100)
620 {
621 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);
622 continue;
623 }
624 PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id];
625 PoolObject plObject = PoolObject(child_pool_id, chance);
626 PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id];
627 plgroup.SetPoolId(mother_pool_id);
628 plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit);
629 SearchPair p(child_pool_id, mother_pool_id);
630 mPoolSearchMap.insert(p);
631
632 ++count;
633 }
634 while (result->NextRow());
635
636 // Now check for circular reference
637 // All pool_ids are in pool_template
638 for (auto const& it : mPoolTemplate)
639 {
640 std::set<uint32> checkedPools;
641 for (SearchMap::iterator poolItr = mPoolSearchMap.find(it.first); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second))
642 {
643 checkedPools.insert(poolItr->first);
644 if (checkedPools.find(poolItr->second) != checkedPools.end())
645 {
646 std::ostringstream ss;
647 ss << "The pool(s) ";
648 for (std::set<uint32>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr)
649 ss << *itr << ' ';
650 ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool "
651 << poolItr->first << " and child pool " << poolItr->second;
652 TC_LOG_ERROR("sql.sql", "{}", ss.str());
653 mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first);
654 mPoolSearchMap.erase(poolItr);
655 --count;
656 break;
657 }
658 }
659 }
660
661 TC_LOG_INFO("server.loading", ">> Loaded {} pools in mother pools in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
662 }
663 }
664
665 // 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
666 TC_LOG_INFO("server.loading", "Starting objects pooling system...");
667 {
668 uint32 oldMSTime = getMSTime();
669
670 QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_members.spawnId, pool_members.poolSpawnId FROM pool_template"
671 " LEFT JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry"
672 " LEFT JOIN pool_members ON pool_members.type = 2 AND pool_template.entry = pool_members.spawnId WHERE game_event_pool.pool_entry IS NULL");
673
674 if (!result)
675 {
676 TC_LOG_INFO("server.loading", ">> Pool handling system initialized, 0 pools spawned.");
677 }
678 else
679 {
680 uint32 count = 0;
681 do
682 {
683 Field* fields = result->Fetch();
684 uint32 pool_entry = fields[0].GetUInt32();
685 uint32 pool_pool_id = fields[1].GetUInt32();
686
687 if (!CheckPool(pool_entry))
688 {
689 if (pool_pool_id)
690 // 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,
691 // however that could recursively invalidate entire chain of mother pools. It can be done in the future but for now we'll do nothing.
692 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());
693 else
694 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);
695 continue;
696 }
697
698 // Don't spawn child pools, they are spawned recursively by their parent pools
699 if (!pool_pool_id)
700 {
701 SpawnPool(pool_entry);
702 count++;
703 }
704 }
705 while (result->NextRow());
706
707 TC_LOG_DEBUG("pool", "Pool handling system initialized, {} pools spawned in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
708
709 }
710 }
711}
712
713// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
714// If it's same, the creature is respawned only (added back to map)
715template<>
716void PoolMgr::SpawnPool<Creature>(uint32 pool_id, uint32 db_guid)
717{
718 auto it = mPoolCreatureGroups.find(pool_id);
719 if (it != mPoolCreatureGroups.end() && !it->second.isEmpty())
720 it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
721}
722
723// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
724// If it's same, the gameobject is respawned only (added back to map)
725template<>
726void PoolMgr::SpawnPool<GameObject>(uint32 pool_id, uint32 db_guid)
727{
728 auto it = mPoolGameobjectGroups.find(pool_id);
729 if (it != mPoolGameobjectGroups.end() && !it->second.isEmpty())
730 it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
731}
732
733// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
734// If it's same, the pool is respawned only
735template<>
736void PoolMgr::SpawnPool<Pool>(uint32 pool_id, uint32 sub_pool_id)
737{
738 auto it = mPoolPoolGroups.find(pool_id);
739 if (it != mPoolPoolGroups.end() && !it->second.isEmpty())
740 it->second.SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
741}
742
744{
745 SpawnPool<Pool>(pool_id, 0);
746 SpawnPool<GameObject>(pool_id, 0);
747 SpawnPool<Creature>(pool_id, 0);
748}
749
750// Call to despawn a pool, all gameobjects/creatures in this pool are removed
751void PoolMgr::DespawnPool(uint32 pool_id, bool alwaysDeleteRespawnTime)
752{
753 {
754 auto it = mPoolCreatureGroups.find(pool_id);
755 if (it != mPoolCreatureGroups.end() && !it->second.isEmpty())
756 it->second.DespawnAllObjects(mSpawnedData, alwaysDeleteRespawnTime);
757 }
758 {
759 auto it = mPoolGameobjectGroups.find(pool_id);
760 if (it != mPoolGameobjectGroups.end() && !it->second.isEmpty())
761 it->second.DespawnAllObjects(mSpawnedData, alwaysDeleteRespawnTime);
762 }
763 {
764 auto it = mPoolPoolGroups.find(pool_id);
765 if (it != mPoolPoolGroups.end() && !it->second.isEmpty())
766 it->second.DespawnAllObjects(mSpawnedData, alwaysDeleteRespawnTime);
767 }
768}
769
770// Selects proper template overload to call based on passed type
772{
773 switch (type)
774 {
776 return IsPartOfAPool<Creature>(spawnId);
778 return IsPartOfAPool<GameObject>(spawnId);
779 default:
780 ABORT_MSG("Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId %u)", uint32(type), spawnId);
781 return 0;
782 }
783}
784
785// Method that check chance integrity of the creatures and gameobjects in this pool
786bool PoolMgr::CheckPool(uint32 pool_id) const
787{
788 {
789 auto it = mPoolGameobjectGroups.find(pool_id);
790 if (it != mPoolGameobjectGroups.end() && !it->second.CheckPool())
791 return false;
792 }
793 {
794 auto it = mPoolCreatureGroups.find(pool_id);
795 if (it != mPoolCreatureGroups.end() && !it->second.CheckPool())
796 return false;
797 }
798 {
799 auto it = mPoolPoolGroups.find(pool_id);
800 if (it != mPoolPoolGroups.end() && !it->second.CheckPool())
801 return false;
802 }
803 return true;
804}
805
806// Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
807// Here we cache only the creature/gameobject whose guid is passed as parameter
808// Then the spawn pool call will use this cache to decide
809template<typename T>
810void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
811{
812 if (uint32 motherpoolid = IsPartOfAPool<Pool>(pool_id))
813 SpawnPool<Pool>(motherpoolid, pool_id);
814 else
815 SpawnPool<T>(pool_id, db_guid_or_pool_id);
816}
817
818template void PoolMgr::UpdatePool<Pool>(uint32 pool_id, uint32 db_guid_or_pool_id);
819template void PoolMgr::UpdatePool<GameObject>(uint32 pool_id, uint32 db_guid_or_pool_id);
820template void PoolMgr::UpdatePool<Creature>(uint32 pool_id, uint32 db_guid_or_pool_id);
821
823{
824 switch (type)
825 {
827 UpdatePool<Creature>(pool_id, spawnId);
828 break;
830 UpdatePool<GameObject>(pool_id, spawnId);
831 break;
832 default:
833 ABORT_MSG("Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId %u)", uint32(type), spawnId);
834 }
835}
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
#define TC_GAME_API
Definition Define.h:114
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#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 sMapMgr
Definition MapManager.h:211
#define sObjectMgr
Definition ObjectMgr.h:1721
#define sPoolMgr
Definition PoolMgr.h:148
double rand_chance()
Definition Random.cpp:81
@ GAMEOBJECT_TYPE_CHEST
@ GAMEOBJECT_TYPE_FISHINGHOLE
@ GAMEOBJECT_TYPE_GOOBER
SpawnObjectType
Definition SpawnData.h:30
@ SPAWN_TYPE_GAMEOBJECT
Definition SpawnData.h:32
@ SPAWN_TYPE_CREATURE
Definition SpawnData.h:31
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
bool IsActiveObject(uint32 db_guid_or_pool_id) const
void RemoveObject(uint32 db_guid_or_pool_id, uint32 pool_id)
uint32 GetActiveObjectCount(uint32 pool_id) const
Definition PoolMgr.cpp:29
ActivePoolObjects mSpawnedCreatures
Definition PoolMgr.h:59
ActivePoolPools mSpawnedPools
Definition PoolMgr.h:61
void ActivateObject(uint32 db_guid_or_pool_id, uint32 pool_id)
ActivePoolObjects mSpawnedGameobjects
Definition PoolMgr.h:60
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool allowDuplicate)
bool GetRespawnCompatibilityMode()
Definition Creature.h:331
void SaveRespawnTime(uint32 forceDelay=0)
Class used to access individual fields of database query result.
Definition Field.h:92
float GetFloat() const
Definition Field.cpp:93
uint32 GetUInt32() const
Definition Field.cpp:61
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool=true)
bool isSpawnedByDefault() const
Definition GameObject.h:161
void SaveRespawnTime(uint32 forceDelay=0)
bool GetRespawnCompatibilityMode()
Definition GameObject.h:271
Definition Map.h:281
bool AddToMap(T *)
Definition Map.cpp:630
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr, bool alwaysDeleteFromDB=false)
Definition Map.h:753
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition Map.h:496
bool Instanceable() const
Definition Map.cpp:4226
bool IsGridLoaded(uint32 gridId) const
Definition Map.h:330
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition Map.h:492
uint32 LowType
Definition ObjectGuid.h:142
void SetPoolId(uint32 pool_id)
Definition PoolMgr.h:70
void SpawnObject(ActivePoolData &spawns, uint32 limit, uint32 triggerFrom)
Definition PoolMgr.cpp:243
void DespawnAllObjects(ActivePoolData &spawns, bool alwaysDeleteRespawnTime=false)
Definition PoolMgr.cpp:147
void RemoveOneRelation(uint32 child_pool_id)
void Spawn1Object(PoolObject *obj)
void AddEntry(PoolObject &poolitem, uint32 maxentries)
Definition PoolMgr.cpp:109
void DespawnObject(ActivePoolData &spawns, ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime=false)
Definition PoolMgr.cpp:134
bool CheckPool() const
Definition PoolMgr.cpp:119
void Despawn1Object(ObjectGuid::LowType guid, bool alwaysDeleteRespawnTime=false, bool saveRespawnTime=true)
std::vector< PoolObject > PoolObjectList
Definition PoolMgr.h:67
void RemoveRespawnTimeFromDB(ObjectGuid::LowType guid)
std::pair< uint32, uint32 > SearchPair
Definition PoolMgr.h:133
bool CheckPool(uint32 pool_id) const
Definition PoolMgr.cpp:786
void Initialize()
Definition PoolMgr.cpp:404
static PoolMgr * instance()
Definition PoolMgr.cpp:410
PoolGroupCreatureMap mPoolCreatureGroups
Definition PoolMgr.h:137
void LoadFromDB()
Definition PoolMgr.cpp:416
PoolGroupPoolMap mPoolPoolGroups
Definition PoolMgr.h:139
uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const
SearchMap mGameobjectSearchMap
Definition PoolMgr.h:141
PoolTemplateDataMap mPoolTemplate
Definition PoolMgr.h:136
void DespawnPool(uint32 pool_id, bool alwaysDeleteRespawnTime=false)
Definition PoolMgr.cpp:751
void SpawnPool(uint32 pool_id)
Definition PoolMgr.cpp:743
void UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
Definition PoolMgr.cpp:810
SearchMap mPoolSearchMap
Definition PoolMgr.h:142
PoolGroupGameObjectMap mPoolGameobjectGroups
Definition PoolMgr.h:138
ActivePoolData mSpawnedData
Definition PoolMgr.h:145
SearchMap mCreatureSearchMap
Definition PoolMgr.h:140
void AddObjectToRemoveList()
Definition Object.cpp:1838
void RandomResize(C &container, std::size_t requestedSize)
Definition Containers.h:66
ObjectGuid::LowType guid
Definition PoolMgr.h:33
float chance
Definition PoolMgr.h:34
uint32 MaxLimit
Definition PoolMgr.h:28
uint32 id
Definition SpawnData.h:96