Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
PoolMgr.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "Containers.h"
7#include "Log.h"
8#include "MapManager.h"
9#include "ObjectMgr.h"
10#include "PoolMgr.h"
11
13// template class ActivePoolData
14
15// Method that tell amount spawned objects/subpools
17{
18 ActivePoolPools::const_iterator itr = mSpawnedPools.find(pool_id);
19 return itr != mSpawnedPools.end() ? itr->second : 0;
20}
21
22// Method that tell if a creature is spawned currently
23template<>
25{
26 return mSpawnedCreatures.find(db_guid) != mSpawnedCreatures.end();
27}
28
29// Method that tell if a gameobject is spawned currently
30template<>
32{
33 return mSpawnedGameobjects.find(db_guid) != mSpawnedGameobjects.end();
34}
35
36// Method that tell if a pool is spawned currently
37template<>
39{
40 return mSpawnedPools.find(sub_pool_id) != mSpawnedPools.end();
41}
42
43// Method that tell if a quest can be started
44template<>
46{
47 return mActiveQuests.find(quest_id) != mActiveQuests.end();
48}
49
50template<>
52{
53 mSpawnedCreatures.insert(db_guid);
54 ++mSpawnedPools[pool_id];
55}
56
57template<>
59{
60 mSpawnedGameobjects.insert(db_guid);
61 ++mSpawnedPools[pool_id];
62}
63
64template<>
66{
67 mSpawnedPools[sub_pool_id] = 0;
68 ++mSpawnedPools[pool_id];
69}
70
71template<>
73{
74 mActiveQuests.insert(quest_id);
75 ++mSpawnedPools[pool_id];
76}
77
78template<>
80{
81 mSpawnedCreatures.erase(db_guid);
82 uint32& val = mSpawnedPools[pool_id];
83 if (val > 0)
84 --val;
85}
86
87template<>
89{
90 mSpawnedGameobjects.erase(db_guid);
91 uint32& val = mSpawnedPools[pool_id];
92 if (val > 0)
93 --val;
94}
95
96template<>
98{
99 mSpawnedPools.erase(sub_pool_id);
100 uint32& val = mSpawnedPools[pool_id];
101 if (val > 0)
102 --val;
103}
104
105template<>
107{
108 mActiveQuests.erase(quest_id);
109 uint32& val = mSpawnedPools[pool_id];
110 if (val > 0)
111 --val;
112}
113
115// Methods of template class PoolGroup
116
117// Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
118template <class T>
119void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
120{
121 if (poolitem.chance != 0 && maxentries == 1)
122 ExplicitlyChanced.push_back(poolitem);
123 else
124 EqualChanced.push_back(poolitem);
125}
126
127// Method to check the chances are proper in this object pool
128template <class T>
130{
131 if (EqualChanced.empty())
132 {
133 float chance = 0;
134 for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
135 chance += ExplicitlyChanced[i].chance;
136 if (chance != 100 && chance != 0)
137 return false;
138 }
139 return true;
140}
141
142template <class T>
144{
145 if (!ExplicitlyChanced.empty())
146 {
147 float roll = (float)rand_chance();
148
149 for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i)
150 {
151 roll -= ExplicitlyChanced[i].chance;
152 // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
153 // so this need explicit check for this case
154 if (roll < 0 && (ExplicitlyChanced[i].guid == triggerFrom || !spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid)))
155 return &ExplicitlyChanced[i];
156 }
157 }
158 if (!EqualChanced.empty())
159 {
160 int32 index = std::rand() % EqualChanced.size();
161 // Triggering object is marked as spawned at this time and can be also rolled (respawn case)
162 // so this need explicit check for this case
163 if (EqualChanced[index].guid == triggerFrom || !spawns.IsActiveObject<T>(EqualChanced[index].guid))
164 return &EqualChanced[index];
165 }
166
167 return NULL;
168}
169
170// Main method to despawn a creature or gameobject in a pool
171// If no guid is passed, the pool is just removed (event end case)
172// If guid is filled, cache will be used and no removal will occur, it just fill the cache
173template<class T>
175{
176 for (size_t i = 0; i < EqualChanced.size(); ++i)
177 {
178 // if spawned
179 if (spawns.IsActiveObject<T>(EqualChanced[i].guid))
180 {
181 if (!guid || EqualChanced[i].guid == guid)
182 {
184 spawns.RemoveObject<T>(EqualChanced[i].guid, poolId);
185 }
186 }
187 }
188
189 for (size_t i = 0; i < ExplicitlyChanced.size(); ++i)
190 {
191 // spawned
192 if (spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid))
193 {
194 if (!guid || ExplicitlyChanced[i].guid == guid)
195 {
197 spawns.RemoveObject<T>(ExplicitlyChanced[i].guid, poolId);
198 }
199 }
200 }
201}
202
203// Method that is actualy doing the removal job on one creature
204template<>
206{
207 if (CreatureData const* data = sObjectMgr->GetCreatureData(guid))
208 {
209 sObjectMgr->RemoveCreatureFromGrid(guid, data);
210
211 if (Creature* creature = ObjectAccessor::GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL))
212 creature->AddObjectToRemoveList();
213 }
214}
215
216// Same on one gameobject
217template<>
219{
220 if (GameObjectData const* data = sObjectMgr->GetGOData(guid))
221 {
222 sObjectMgr->RemoveGameobjectFromGrid(guid, data);
223
225 pGameobject->AddObjectToRemoveList();
226 }
227}
228
229// Same on one pool
230template<>
232{
233 sPoolMgr->DespawnPool(child_pool_id);
234}
235
236// Same on one quest
237template<>
239{
240 // Creatures
241 QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap();
242 PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(quest_id);
243 for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
244 {
245 QuestRelations::iterator qitr = questMap->find(itr->second);
246 if (qitr == questMap->end())
247 continue;
248 QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
249 for (; qitr != lastElement; ++qitr)
250 {
251 if (qitr->first == itr->second && qitr->second == itr->first)
252 {
253 questMap->erase(qitr); // iterator is now no more valid
254 break; // but we can exit loop since the element is found
255 }
256 }
257 }
258
259 // Gameobjects
260 questMap = sObjectMgr->GetGOQuestRelationMap();
261 qr = sPoolMgr->mQuestGORelation.equal_range(quest_id);
262 for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
263 {
264 QuestRelations::iterator qitr = questMap->find(itr->second);
265 if (qitr == questMap->end())
266 continue;
267 QuestRelations::iterator lastElement = questMap->upper_bound(itr->second);
268 for (; qitr != lastElement; ++qitr)
269 {
270 if (qitr->first == itr->second && qitr->second == itr->first)
271 {
272 questMap->erase(qitr); // iterator is now no more valid
273 break; // but we can exit loop since the element is found
274 }
275 }
276 }
277}
278
279// Method for a pool only to remove any found record causing a circular dependency loop
280template<>
282{
283 for (PoolObjectList::iterator itr = ExplicitlyChanced.begin(); itr != ExplicitlyChanced.end(); ++itr)
284 {
285 if (itr->guid == child_pool_id)
286 {
287 ExplicitlyChanced.erase(itr);
288 break;
289 }
290 }
291 for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
292 {
293 if (itr->guid == child_pool_id)
294 {
295 EqualChanced.erase(itr);
296 break;
297 }
298 }
299}
300
301template <class T>
302void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom)
303{
304 uint32 lastDespawned = 0;
305 int count = limit - spawns.GetActiveObjectCount(poolId);
306
307 // If triggered from some object respawn this object is still marked as spawned
308 // and also counted into m_SpawnedPoolAmount so we need increase count to be
309 // spawned by 1
310 if (triggerFrom)
311 ++count;
312
313 // This will try to spawn the rest of pool, not guaranteed
314 for (int i = 0; i < count; ++i)
315 {
316 PoolObject* obj = RollOne(spawns, triggerFrom);
317 if (!obj)
318 continue;
319 if (obj->guid == lastDespawned)
320 continue;
321
322 if (obj->guid == triggerFrom)
323 {
324 ReSpawn1Object(obj);
325 triggerFrom = 0;
326 continue;
327 }
328 spawns.ActivateObject<T>(obj->guid, poolId);
329 Spawn1Object(obj);
330
331 if (triggerFrom)
332 {
333 // One spawn one despawn no count increase
334 DespawnObject(spawns, triggerFrom);
335 lastDespawned = triggerFrom;
336 triggerFrom = 0;
337 }
338 }
339}
340
341// Method that is actualy doing the spawn job on 1 creature
342template <>
344{
345 if (CreatureData const* data = sObjectMgr->GetCreatureData(obj->guid))
346 {
347 sObjectMgr->AddCreatureToGrid(obj->guid, data);
348
349 // Spawn if necessary (loaded grids only)
350 Map* map = sMapMgr->CreateBaseMap(data->mapid);
351 // We use spawn coords to spawn
352 if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
353 {
354 Creature* creature = new Creature;
355 //SF_LOG_DEBUG("pool", "Spawning creature %u", guid);
356 if (!creature->LoadCreatureFromDB(obj->guid, map))
357 {
358 delete creature;
359 return;
360 }
361 }
362 }
363}
364
365// Same for 1 gameobject
366template <>
368{
369 if (GameObjectData const* data = sObjectMgr->GetGOData(obj->guid))
370 {
371 sObjectMgr->AddGameobjectToGrid(obj->guid, data);
372 // Spawn if necessary (loaded grids only)
373 // this base map checked as non-instanced and then only existed
374 Map* map = sMapMgr->CreateBaseMap(data->mapid);
375 // We use current coords to unspawn, not spawn coords since creature can have changed grid
376 if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY))
377 {
378 GameObject* pGameobject = new GameObject;
379 //SF_LOG_DEBUG("pool", "Spawning gameobject %u", guid);
380 if (!pGameobject->LoadGameObjectFromDB(obj->guid, map, false))
381 {
382 delete pGameobject;
383 return;
384 }
385 else
386 {
387 if (pGameobject->isSpawnedByDefault())
388 map->AddToMap(pGameobject);
389 }
390 }
391 }
392}
393
394// Same for 1 pool
395template <>
397{
398 sPoolMgr->SpawnPool(obj->guid);
399}
400
401// Same for 1 quest
402template<>
404{
405 // Creatures
406 QuestRelations* questMap = sObjectMgr->GetCreatureQuestRelationMap();
407 PooledQuestRelationBoundsNC qr = sPoolMgr->mQuestCreatureRelation.equal_range(obj->guid);
408 for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
409 {
410 SF_LOG_DEBUG("pool", "PoolGroup<Quest>: Adding quest %u to creature %u", itr->first, itr->second);
411 questMap->insert(QuestRelations::value_type(itr->second, itr->first));
412 }
413
414 // Gameobjects
415 questMap = sObjectMgr->GetGOQuestRelationMap();
416 qr = sPoolMgr->mQuestGORelation.equal_range(obj->guid);
417 for (PooledQuestRelation::iterator itr = qr.first; itr != qr.second; ++itr)
418 {
419 SF_LOG_DEBUG("pool", "PoolGroup<Quest>: Adding quest %u to GO %u", itr->first, itr->second);
420 questMap->insert(QuestRelations::value_type(itr->second, itr->first));
421 }
422}
423
424template <>
426{
427 SF_LOG_DEBUG("pool", "PoolGroup<Quest>: Spawning pool %u", poolId);
428 // load state from db
429 if (!triggerFrom)
430 {
432
433 stmt->setUInt32(0, poolId);
434
435 PreparedQueryResult result = CharacterDatabase.Query(stmt);
436
437 if (result)
438 {
439 do
440 {
441 uint32 questId = result->Fetch()[0].GetUInt32();
442 spawns.ActivateObject<Quest>(questId, poolId);
443 PoolObject tempObj(questId, 0.0f);
444 Spawn1Object(&tempObj);
445 --limit;
446 } while (result->NextRow() && limit);
447 return;
448 }
449 }
450
451 ActivePoolObjects currentQuests = spawns.GetActiveQuests();
452 ActivePoolObjects newQuests;
453
454 // always try to select different quests
455 for (PoolObjectList::iterator itr = EqualChanced.begin(); itr != EqualChanced.end(); ++itr)
456 {
457 if (spawns.IsActiveObject<Quest>(itr->guid))
458 continue;
459 newQuests.insert(itr->guid);
460 }
461
462 // clear the pool
463 DespawnObject(spawns);
464
465 // recycle minimal amount of quests if possible count is lower than limit
466 if (limit > newQuests.size() && !currentQuests.empty())
467 {
468 do
469 {
471 newQuests.insert(questId);
472 currentQuests.erase(questId);
473 } while (newQuests.size() < limit && !currentQuests.empty()); // failsafe
474 }
475
476 if (newQuests.empty())
477 return;
478
479 // activate <limit> random quests
480 do
481 {
483 spawns.ActivateObject<Quest>(questId, poolId);
484 PoolObject tempObj(questId, 0.0f);
485 Spawn1Object(&tempObj);
486 newQuests.erase(questId);
487 --limit;
488 } while (limit && !newQuests.empty());
489
490 // if we are here it means the pool is initialized at startup and did not have previous saved state
491 if (!triggerFrom)
492 sPoolMgr->SaveQuestsToDB();
493}
494
495// Method that does the respawn job on the specified creature
496template <>
498{
499 if (CreatureData const* data = sObjectMgr->GetCreatureData(obj->guid))
500 if (Creature* creature = ObjectAccessor::GetObjectInWorld(MAKE_NEW_GUID(obj->guid, data->id, HIGHGUID_UNIT), (Creature*)NULL))
501 creature->GetMap()->AddToMap(creature);
502}
503
504// Method that does the respawn job on the specified gameobject
505template <>
507{
508 if (GameObjectData const* data = sObjectMgr->GetGOData(obj->guid))
510 pGameobject->GetMap()->AddToMap(pGameobject);
511}
512
513// Nothing to do for a child Pool
514template <>
516
517// Nothing to do for a quest
518template <>
520
522// Methods of class PoolMgr
523
525
527{
528 QueryResult result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template");
529 if (result)
530 {
531 Field* fields = result->Fetch();
532 max_pool_id = fields[0].GetUInt32();
533 }
534
535 mPoolTemplate.resize(max_pool_id + 1);
538 mPoolPoolGroups.resize(max_pool_id + 1);
539 mPoolQuestGroups.resize(max_pool_id + 1);
540
541 mQuestSearchMap.clear();
542 mGameobjectSearchMap.clear();
543 mCreatureSearchMap.clear();
544}
545
547{
548 // Pool templates
549 {
550 uint32 oldMSTime = getMSTime();
551
552 QueryResult result = WorldDatabase.Query("SELECT entry, max_limit FROM pool_template");
553 if (!result)
554 {
555 mPoolTemplate.clear();
556 SF_LOG_INFO("server.loading", ">> Loaded 0 object pools. DB table `pool_template` is empty.");
557 return;
558 }
559
560 uint32 count = 0;
561 do
562 {
563 Field* fields = result->Fetch();
564
565 uint32 pool_id = fields[0].GetUInt32();
566
567 PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id];
568 pPoolTemplate.MaxLimit = fields[1].GetUInt32();
569
570 ++count;
571 } while (result->NextRow());
572
573 SF_LOG_INFO("server.loading", ">> Loaded %u objects pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
574 }
575
576 // Creatures
577
578 SF_LOG_INFO("server.loading", "Loading Creatures Pooling Data...");
579 {
580 uint32 oldMSTime = getMSTime();
581
582 // 1 2 3
583 QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature");
584
585 if (!result)
586 {
587 SF_LOG_INFO("server.loading", ">> Loaded 0 creatures in pools. DB table `pool_creature` is empty.");
588 }
589 else
590 {
591 uint32 count = 0;
592 do
593 {
594 Field* fields = result->Fetch();
595
596 uint32 guid = fields[0].GetUInt32();
597 uint32 pool_id = fields[1].GetUInt32();
598 float chance = fields[2].GetFloat();
599
600 CreatureData const* data = sObjectMgr->GetCreatureData(guid);
601 if (!data)
602 {
603 SF_LOG_ERROR("sql.sql", "`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id);
604 continue;
605 }
606 if (pool_id > max_pool_id)
607 {
608 SF_LOG_ERROR("sql.sql", "`pool_creature` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
609 continue;
610 }
611 if (chance < 0 || chance > 100)
612 {
613 SF_LOG_ERROR("sql.sql", "`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%u), skipped.", chance, guid, pool_id);
614 continue;
615 }
616 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
617 PoolObject plObject = PoolObject(guid, chance);
618 PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id];
619 cregroup.SetPoolId(pool_id);
620 cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
621 SearchPair p(guid, pool_id);
622 mCreatureSearchMap.insert(p);
623
624 ++count;
625 } while (result->NextRow());
626
627 SF_LOG_INFO("server.loading", ">> Loaded %u creatures in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
628 }
629 }
630
631 // Gameobjects
632
633 SF_LOG_INFO("server.loading", "Loading Gameobject Pooling Data...");
634 {
635 uint32 oldMSTime = getMSTime();
636
637 // 1 2 3
638 QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject");
639
640 if (!result)
641 {
642 SF_LOG_INFO("server.loading", ">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty.");
643 }
644 else
645 {
646 uint32 count = 0;
647 do
648 {
649 Field* fields = result->Fetch();
650
651 uint32 guid = fields[0].GetUInt32();
652 uint32 pool_id = fields[1].GetUInt32();
653 float chance = fields[2].GetFloat();
654
655 GameObjectData const* data = sObjectMgr->GetGOData(guid);
656 if (!data)
657 {
658 SF_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id);
659 continue;
660 }
661
662 GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(data->id);
663 if (goinfo->type != GAMEOBJECT_TYPE_CHEST &&
664 goinfo->type != GAMEOBJECT_TYPE_GOOBER &&
666 {
667 SF_LOG_ERROR("sql.sql", "`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid, goinfo->type, pool_id);
668 continue;
669 }
670
671 if (pool_id > max_pool_id)
672 {
673 SF_LOG_ERROR("sql.sql", "`pool_gameobject` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
674 continue;
675 }
676
677 if (chance < 0 || chance > 100)
678 {
679 SF_LOG_ERROR("sql.sql", "`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%u), skipped.", chance, guid, pool_id);
680 continue;
681 }
682
683 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
684 PoolObject plObject = PoolObject(guid, chance);
686 gogroup.SetPoolId(pool_id);
687 gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
688 SearchPair p(guid, pool_id);
689 mGameobjectSearchMap.insert(p);
690
691 ++count;
692 } while (result->NextRow());
693
694 SF_LOG_INFO("server.loading", ">> Loaded %u gameobject in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
695 }
696 }
697
698 // Pool of pools
699
700 SF_LOG_INFO("server.loading", "Loading Mother Pooling Data...");
701 {
702 uint32 oldMSTime = getMSTime();
703
704 // 1 2 3
705 QueryResult result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool");
706
707 if (!result)
708 {
709 SF_LOG_INFO("server.loading", ">> Loaded 0 pools in pools");
710 }
711 else
712 {
713 uint32 count = 0;
714 do
715 {
716 Field* fields = result->Fetch();
717
718 uint32 child_pool_id = fields[0].GetUInt32();
719 uint32 mother_pool_id = fields[1].GetUInt32();
720 float chance = fields[2].GetFloat();
721
722 if (mother_pool_id > max_pool_id)
723 {
724 SF_LOG_ERROR("sql.sql", "`pool_pool` mother_pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", mother_pool_id);
725 continue;
726 }
727 if (child_pool_id > max_pool_id)
728 {
729 SF_LOG_ERROR("sql.sql", "`pool_pool` included pool_id (%u) is out of range compared to max pool id in `pool_template`, skipped.", child_pool_id);
730 continue;
731 }
732 if (mother_pool_id == child_pool_id)
733 {
734 SF_LOG_ERROR("sql.sql", "`pool_pool` pool_id (%u) includes itself, dead-lock detected, skipped.", child_pool_id);
735 continue;
736 }
737 if (chance < 0 || chance > 100)
738 {
739 SF_LOG_ERROR("sql.sql", "`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%u), skipped.", chance, child_pool_id, mother_pool_id);
740 continue;
741 }
742 PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id];
743 PoolObject plObject = PoolObject(child_pool_id, chance);
744 PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id];
745 plgroup.SetPoolId(mother_pool_id);
746 plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit);
747 SearchPair p(child_pool_id, mother_pool_id);
748 mPoolSearchMap.insert(p);
749
750 ++count;
751 } while (result->NextRow());
752
753 // Now check for circular reference
754 for (uint32 i = 0; i < max_pool_id; ++i)
755 {
756 std::set<uint32> checkedPools;
757 for (SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second))
758 {
759 checkedPools.insert(poolItr->first);
760 if (checkedPools.find(poolItr->second) != checkedPools.end())
761 {
762 std::ostringstream ss;
763 ss << "The pool(s) ";
764 for (std::set<uint32>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr)
765 ss << *itr << ' ';
766 ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool "
767 << poolItr->first << " and child pool " << poolItr->second;
768 SF_LOG_ERROR("sql.sql", "%s", ss.str().c_str());
769 mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first);
770 mPoolSearchMap.erase(poolItr);
771 --count;
772 break;
773 }
774 }
775 }
776
777 SF_LOG_INFO("server.loading", ">> Loaded %u pools in mother pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
778 }
779 }
780
781 SF_LOG_INFO("server.loading", "Loading Quest Pooling Data...");
782 {
783 uint32 oldMSTime = getMSTime();
784
785 PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_QUEST_POOLS);
786 PreparedQueryResult result = WorldDatabase.Query(stmt);
787
788 if (!result)
789 {
790 SF_LOG_INFO("server.loading", ">> Loaded 0 quests in pools");
791 }
792 else
793 {
796
797 enum eQuestTypes
798 {
799 QUEST_NONE = 0,
800 QUEST_DAILY = 1,
801 QUEST_WEEKLY = 2
802 };
803
804 std::map<uint32, int32> poolTypeMap;
805 uint32 count = 0;
806 do
807 {
808 Field* fields = result->Fetch();
809
810 uint32 entry = fields[0].GetUInt32();
811 uint32 pool_id = fields[1].GetUInt32();
812
813 Quest const* quest = sObjectMgr->GetQuestTemplate(entry);
814 if (!quest)
815 {
816 SF_LOG_ERROR("sql.sql", "`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id);
817 continue;
818 }
819
820 if (pool_id > max_pool_id)
821 {
822 SF_LOG_ERROR("sql.sql", "`pool_quest` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id);
823 continue;
824 }
825
826 if (!quest->IsDailyOrWeekly())
827 {
828 SF_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);
829 continue;
830 }
831
832 if (poolTypeMap[pool_id] == QUEST_NONE)
833 poolTypeMap[pool_id] = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY;
834
835 int32 currType = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY;
836
837 if (poolTypeMap[pool_id] != currType)
838 {
839 SF_LOG_ERROR("sql.sql", "`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.",
840 entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY");
841 continue;
842 }
843
844 creBounds = mQuestCreatureRelation.equal_range(entry);
845 goBounds = mQuestGORelation.equal_range(entry);
846
847 if (creBounds.first == creBounds.second && goBounds.first == goBounds.second)
848 {
849 SF_LOG_ERROR("sql.sql", "`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id);
850 continue;
851 }
852
853 PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id];
854 PoolObject plObject = PoolObject(entry, 0.0f);
855 PoolGroup<Quest>& questgroup = mPoolQuestGroups[pool_id];
856 questgroup.SetPoolId(pool_id);
857 questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
858 SearchPair p(entry, pool_id);
859 mQuestSearchMap.insert(p);
860
861 ++count;
862 } while (result->NextRow());
863
864 SF_LOG_INFO("server.loading", ">> Loaded %u quests in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
865 }
866 }
867
868 // 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
869 SF_LOG_INFO("server.loading", "Starting objects pooling system...");
870 {
871 uint32 oldMSTime = getMSTime();
872
873 QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_pool.pool_id, pool_pool.mother_pool FROM pool_template"
874 " LEFT JOIN game_event_pool ON pool_template.entry=game_event_pool.pool_entry"
875 " LEFT JOIN pool_pool ON pool_template.entry=pool_pool.pool_id WHERE game_event_pool.pool_entry IS NULL");
876
877 if (!result)
878 {
879 SF_LOG_INFO("server.loading", ">> Pool handling system initialized, 0 pools spawned.");
880 }
881 else
882 {
883 uint32 count = 0;
884 do
885 {
886 Field* fields = result->Fetch();
887 uint32 pool_entry = fields[0].GetUInt32();
888 uint32 pool_pool_id = fields[1].GetUInt32();
889
890 if (!CheckPool(pool_entry))
891 {
892 if (pool_pool_id)
893 // 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,
894 // however that could recursively invalidate entire chain of mother pools. It can be done in the future but for now we'll do nothing.
895 SF_LOG_ERROR("sql.sql", "Pool Id %u has no equal chance pooled entites defined and explicit chance sum is not 100. This broken pool is a child pool of Id %u and cannot be safely removed.", pool_entry, fields[2].GetUInt32());
896 else
897 SF_LOG_ERROR("sql.sql", "Pool Id %u has no equal chance pooled entites defined and explicit chance sum is not 100. The pool will not be spawned.", pool_entry);
898 continue;
899 }
900
901 // Don't spawn child pools, they are spawned recursively by their parent pools
902 if (!pool_pool_id)
903 {
904 SpawnPool(pool_entry);
905 count++;
906 }
907 } while (result->NextRow());
908
909 SF_LOG_DEBUG("pool", "Pool handling system initialized, %u pools spawned in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
910 }
911 }
912}
913
915
917{
918 SQLTransaction trans = CharacterDatabase.BeginTransaction();
919
920 for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
921 {
922 if (itr->isEmpty())
923 continue;
925 stmt->setUInt32(0, itr->GetPoolId());
926 trans->Append(stmt);
927 }
928
929 for (SearchMap::iterator itr = mQuestSearchMap.begin(); itr != mQuestSearchMap.end(); ++itr)
930 {
931 if (IsSpawnedObject<Quest>(itr->first))
932 {
934 stmt->setUInt32(0, itr->second);
935 stmt->setUInt32(1, itr->first);
936 trans->Append(stmt);
937 }
938 }
939
940 CharacterDatabase.CommitTransaction(trans);
941}
942
944{
945 for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
946 {
947 if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->GetFirstEqualChancedObjectId()))
948 {
949 if (quest->IsWeekly())
950 continue;
951
952 UpdatePool<Quest>(itr->GetPoolId(), 1); // anything non-zero means don't load from db
953 }
954 }
955
957}
958
960{
961 for (PoolGroupQuestMap::iterator itr = mPoolQuestGroups.begin(); itr != mPoolQuestGroups.end(); ++itr)
962 {
963 if (Quest const* quest = sObjectMgr->GetQuestTemplate(itr->GetFirstEqualChancedObjectId()))
964 {
965 if (quest->IsDaily())
966 continue;
967
968 UpdatePool<Quest>(itr->GetPoolId(), 1);
969 }
970 }
971
973}
974
975// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
976// If it's same, the creature is respawned only (added back to map)
977template<>
979{
980 if (!mPoolCreatureGroups[pool_id].isEmpty())
981 mPoolCreatureGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
982}
983
984// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
985// If it's same, the gameobject is respawned only (added back to map)
986template<>
988{
989 if (!mPoolGameobjectGroups[pool_id].isEmpty())
990 mPoolGameobjectGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, db_guid);
991}
992
993// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
994// If it's same, the pool is respawned only
995template<>
996void PoolMgr::SpawnPool<Pool>(uint32 pool_id, uint32 sub_pool_id)
997{
998 if (!mPoolPoolGroups[pool_id].isEmpty())
999 mPoolPoolGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, sub_pool_id);
1000}
1001
1002// Call to spawn a pool
1003template<>
1005{
1006 if (!mPoolQuestGroups[pool_id].isEmpty())
1007 mPoolQuestGroups[pool_id].SpawnObject(mSpawnedData, mPoolTemplate[pool_id].MaxLimit, quest_id);
1008}
1009
1011{
1012 SpawnPool<Pool>(pool_id, 0);
1013 SpawnPool<GameObject>(pool_id, 0);
1014 SpawnPool<Creature>(pool_id, 0);
1015 SpawnPool<Quest>(pool_id, 0);
1016}
1017
1018// Call to despawn a pool, all gameobjects/creatures in this pool are removed
1020{
1021 if (!mPoolCreatureGroups[pool_id].isEmpty())
1022 mPoolCreatureGroups[pool_id].DespawnObject(mSpawnedData);
1023
1024 if (!mPoolGameobjectGroups[pool_id].isEmpty())
1025 mPoolGameobjectGroups[pool_id].DespawnObject(mSpawnedData);
1026
1027 if (!mPoolPoolGroups[pool_id].isEmpty())
1028 mPoolPoolGroups[pool_id].DespawnObject(mSpawnedData);
1029
1030 if (!mPoolQuestGroups[pool_id].isEmpty())
1031 mPoolQuestGroups[pool_id].DespawnObject(mSpawnedData);
1032}
1033
1034// Method that check chance integrity of the creatures and gameobjects in this pool
1035bool PoolMgr::CheckPool(uint32 pool_id) const
1036{
1037 return pool_id <= max_pool_id &&
1038 mPoolGameobjectGroups[pool_id].CheckPool() &&
1039 mPoolCreatureGroups[pool_id].CheckPool() &&
1040 mPoolPoolGroups[pool_id].CheckPool() &&
1041 mPoolQuestGroups[pool_id].CheckPool();
1042}
1043
1044// Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
1045// Here we cache only the creature/gameobject whose guid is passed as parameter
1046// Then the spawn pool call will use this cache to decide
1047template<typename T>
1048void PoolMgr::UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
1049{
1050 if (uint32 motherpoolid = IsPartOfAPool<Pool>(pool_id))
1051 SpawnPool<Pool>(motherpoolid, pool_id);
1052 else
1053 SpawnPool<T>(pool_id, db_guid_or_pool_id);
1054}
1055
1056template void PoolMgr::UpdatePool<Pool>(uint32 pool_id, uint32 db_guid_or_pool_id);
1057template void PoolMgr::UpdatePool<GameObject>(uint32 pool_id, uint32 db_guid_or_pool_id);
1058template void PoolMgr::UpdatePool<Creature>(uint32 pool_id, uint32 db_guid_or_pool_id);
1059template void PoolMgr::UpdatePool<Quest>(uint32 pool_id, uint32 db_guid_or_pool_id);
@ CHAR_DEL_QUEST_POOL_SAVE
@ CHAR_SEL_POOL_QUEST_SAVE
@ CHAR_INS_QUEST_POOL_SAVE
std::int32_t int32
Definition Define.h:73
std::uint32_t uint32
Definition Define.h:77
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define SF_LOG_INFO(filterType__,...)
Definition Log.h:137
#define sMapMgr
Definition MapManager.h:145
@ HIGHGUID_GAMEOBJECT
@ HIGHGUID_UNIT
uint64 MAKE_NEW_GUID(uint32 l, uint32 e, uint32 h)
#define sObjectMgr
Definition ObjectMgr.h:1617
std::multimap< uint32, uint32 > QuestRelations
Definition ObjectMgr.h:455
std::pair< PooledQuestRelation::iterator, PooledQuestRelation::iterator > PooledQuestRelationBoundsNC
Definition PoolMgr.h:90
#define sPoolMgr
Definition PoolMgr.h:154
std::set< uint32 > ActivePoolObjects
Definition PoolMgr.h:31
std::pair< PooledQuestRelation::const_iterator, PooledQuestRelation::const_iterator > PooledQuestRelationBounds
Definition PoolMgr.h:89
Skyfire::AutoPtr< PreparedResultSet, Skyfire::Mutex > PreparedQueryResult
Definition QueryResult.h:94
Skyfire::AutoPtr< ResultSet, Skyfire::Mutex > QueryResult
Definition QueryResult.h:48
@ GAMEOBJECT_TYPE_CHEST
@ GAMEOBJECT_TYPE_FISHINGHOLE
@ GAMEOBJECT_TYPE_GOOBER
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:22
uint32 getMSTime()
Definition Timer.h:12
Skyfire::AutoPtr< Transaction, Skyfire::Mutex > SQLTransaction
Definition Transaction.h:42
double rand_chance(void)
Definition Util.cpp:55
@ WORLD_SEL_QUEST_POOLS
ActivePoolObjects mActiveQuests
Definition PoolMgr.h:52
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:16
ActivePoolObjects mSpawnedCreatures
Definition PoolMgr.h:50
ActivePoolPools mSpawnedPools
Definition PoolMgr.h:53
void ActivateObject(uint32 db_guid_or_pool_id, uint32 pool_id)
ActivePoolObjects mSpawnedGameobjects
Definition PoolMgr.h:51
ActivePoolObjects GetActiveQuests() const
Definition PoolMgr.h:48
bool LoadCreatureFromDB(uint32 guid, Map *map, bool addToMap=true)
Definition Field.h:16
float GetFloat() const
Definition Field.h:177
uint32 GetUInt32() const
Definition Field.h:105
bool isSpawnedByDefault() const
Definition GameObject.h:711
bool LoadGameObjectFromDB(uint32 guid, Map *map, bool addToMap=true)
Definition Map.h:238
bool AddToMap(T *)
Definition Map.cpp:504
bool Instanceable() const
Definition Map.h:367
bool IsGridLoaded(float x, float y) const
Definition Map.h:283
static T * GetObjectInWorld(uint64 guid, T *)
void SetPoolId(uint32 pool_id)
Definition PoolMgr.h:62
void SpawnObject(ActivePoolData &spawns, uint32 limit, uint32 triggerFrom)
Definition PoolMgr.cpp:302
void ReSpawn1Object(PoolObject *obj)
void Despawn1Object(uint32 guid)
void RemoveOneRelation(uint32 child_pool_id)
PoolObjectList ExplicitlyChanced
Definition PoolMgr.h:84
void Spawn1Object(PoolObject *obj)
void AddEntry(PoolObject &poolitem, uint32 maxentries)
Definition PoolMgr.cpp:119
void DespawnObject(ActivePoolData &spawns, uint32 guid=0)
Definition PoolMgr.cpp:174
bool CheckPool() const
Definition PoolMgr.cpp:129
uint32 poolId
Definition PoolMgr.h:83
PoolObjectList EqualChanced
Definition PoolMgr.h:85
PoolObject * RollOne(ActivePoolData &spawns, uint32 triggerFrom)
Definition PoolMgr.cpp:143
std::pair< uint32, uint32 > SearchPair
Definition PoolMgr.h:137
uint32 max_pool_id
Definition PoolMgr.h:131
bool CheckPool(uint32 pool_id) const
Definition PoolMgr.cpp:1035
void Initialize()
Definition PoolMgr.cpp:526
void ChangeWeeklyQuests()
Definition PoolMgr.cpp:959
PoolGroupCreatureMap mPoolCreatureGroups
Definition PoolMgr.h:141
void LoadFromDB()
Definition PoolMgr.cpp:546
void SaveQuestsToDB()
Definition PoolMgr.cpp:916
PoolGroupPoolMap mPoolPoolGroups
Definition PoolMgr.h:143
uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const
SearchMap mGameobjectSearchMap
Definition PoolMgr.h:146
PoolTemplateDataMap mPoolTemplate
Definition PoolMgr.h:140
void LoadQuestPools()
Definition PoolMgr.cpp:914
void SpawnPool(uint32 pool_id)
Definition PoolMgr.cpp:1010
void ChangeDailyQuests()
Definition PoolMgr.cpp:943
void UpdatePool(uint32 pool_id, uint32 db_guid_or_pool_id)
Definition PoolMgr.cpp:1048
SearchMap mPoolSearchMap
Definition PoolMgr.h:147
PoolGroupGameObjectMap mPoolGameobjectGroups
Definition PoolMgr.h:142
ActivePoolData mSpawnedData
Definition PoolMgr.h:151
SearchMap mQuestSearchMap
Definition PoolMgr.h:148
void DespawnPool(uint32 pool_id)
Definition PoolMgr.cpp:1019
PooledQuestRelation mQuestGORelation
Definition PoolMgr.h:125
PooledQuestRelation mQuestCreatureRelation
Definition PoolMgr.h:124
PoolGroupQuestMap mPoolQuestGroups
Definition PoolMgr.h:144
bool IsSpawnedObject(uint32 db_guid_or_pool_id) const
Definition PoolMgr.h:111
SearchMap mCreatureSearchMap
Definition PoolMgr.h:145
void setUInt32(const uint8 index, const uint32 value)
bool IsDaily() const
Definition QuestDef.h:337
bool IsDailyOrWeekly() const
Definition QuestDef.h:341
CharacterDatabaseWorkerPool CharacterDatabase
Accessor to the character database.
Definition Main.cpp:41
WorldDatabaseWorkerPool WorldDatabase
Accessor to the world database.
Definition Main.cpp:40
C::value_type const & SelectRandomContainerElement(C const &container)
Definition Containers.h:48
uint32 guid
Definition PoolMgr.h:22
float chance
Definition PoolMgr.h:23
uint32 MaxLimit
Definition PoolMgr.h:17