void TestPool::testObjectAllocation() { rxx_allocator<PoolObject> alloc; PoolObject *p = alloc.allocate(1); //object is not yet initialized (it's actually zeroed //because the block in the pool is zeroed QCOMPARE(p->foo, 0); alloc.construct(p, PoolObject()); //now we have the object there QCOMPARE(p->foo, 3); alloc.destroy(p); //destructor was called, the "foo" field is zeroed again QCOMPARE(p->foo, 0); }
void PoolManager::LoadFromDB() { QueryResult* result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template"); if (!result) { sLog.outString(">> Table pool_template is empty."); sLog.outString(); return; } else { Field* fields = result->Fetch(); max_pool_id = fields[0].GetUInt16(); delete result; } mPoolTemplate.resize(max_pool_id + 1); result = WorldDatabase.Query("SELECT entry, max_limit, description FROM pool_template"); if (!result) { mPoolTemplate.clear(); sLog.outString(">> Table pool_template is empty:"); sLog.outString(); return; } uint32 count = 0; BarGoLink bar(result->GetRowCount()); do { ++count; Field* fields = result->Fetch(); bar.step(); uint16 pool_id = fields[0].GetUInt16(); PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id]; pPoolTemplate.MaxLimit = fields[1].GetUInt32(); pPoolTemplate.description = fields[2].GetCppString(); pPoolTemplate.AutoSpawn = true; // will update and later data loading } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u objects pools", count); delete result; PoolMapChecker mapChecker(mPoolTemplate); // Creatures (guids and entries) mPoolCreatureGroups.resize(max_pool_id + 1); mCreatureSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); CreatureData const* data = sObjectMgr.GetCreatureData(guid); if (!data) { sLog.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_creature", "creature guid")) continue; PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature`", count); delete result; } result = WorldDatabase.Query("SELECT guid, pool_entry, chance, pool_creature_template.id FROM pool_creature_template LEFT JOIN creature ON creature.id = pool_creature_template.id"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature_template`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); uint16 entry_id = fields[3].GetUInt32(); // for errors output only CreatureData const* data = sObjectMgr.GetCreatureData(guid); if (!data) { sLog.outErrorDb("`pool_creature_template` has a non existing creature spawn (GUID: %u Entry: %u) defined for pool id (%u), skipped.", guid, entry_id, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_creature_template` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_creature_template` has an invalid chance (%f) for creature (Guid %u Entry %u) in pool id (%i), skipped.", chance, guid, entry_id, pool_id); continue; } // `pool_creature` and `pool_creature_template` can't have guids duplicates (in second case because entries also unique) // So if guid already listed in pools then this duplicate from alt.table // Also note: for added guid not important what case we skip from 2 tables if (uint16 alt_pool_id = IsPartOfAPool<Creature>(guid)) { sLog.outErrorDb("`pool_creature` has guid %u for pool %u that already added to pool %u from `pool_creature_template` for creature entry %u, skipped.", guid, pool_id, alt_pool_id, entry_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_creature_template", "creature guid")) continue; PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature_template`", count); delete result; } // Gameobjects (guids and entries) mPoolGameobjectGroups.resize(max_pool_id + 1); mGameobjectSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); GameObjectData const* data = sObjectMgr.GetGOData(guid); if (!data) { sLog.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(data->id); if (goinfo->type != GAMEOBJECT_TYPE_CHEST && goinfo->type != GAMEOBJECT_TYPE_GOOBER && goinfo->type != GAMEOBJECT_TYPE_FISHINGHOLE && goinfo->type != GAMEOBJECT_TYPE_TRAP) { sLog.outErrorDb("`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid, goinfo->type, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_gameobject", "gameobject guid")) continue; PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject`", count); delete result; } // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance, pool_gameobject_template.id FROM pool_gameobject_template LEFT JOIN gameobject ON gameobject.id = pool_gameobject_template.id"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject_template`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); uint16 entry_id = fields[3].GetUInt32(); // for errors output only GameObjectData const* data = sObjectMgr.GetGOData(guid); if (!data) { sLog.outErrorDb("`pool_gameobject_template` has a non existing gameobject spawn (GUID: %u Entry %u) defined for pool id (%u), skipped.", guid, entry_id, pool_id); continue; } GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(data->id); if (goinfo->type != GAMEOBJECT_TYPE_CHEST && goinfo->type != GAMEOBJECT_TYPE_GOOBER && goinfo->type != GAMEOBJECT_TYPE_FISHINGHOLE && goinfo->type != GAMEOBJECT_TYPE_TRAP) { sLog.outErrorDb("`pool_gameobject_template` has a not lootable gameobject spawn (GUID: %u Entry %u Type: %u) defined for pool id (%u), skipped.", guid, entry_id, goinfo->type, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_gameobject_template` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_gameobject_template` has an invalid chance (%f) for gameobject (Guid %u Entry %u) in pool id (%i), skipped.", chance, guid, entry_id, pool_id); continue; } // `pool_gameobject` and `pool_gameobject_template` can't have guids duplicates (in second case because entries also unique) // So if guid already listed in pools then this duplicate from alt.table // Also note: for added guid not important what case we skip from 2 tables if (uint16 alt_pool_id = IsPartOfAPool<GameObject>(guid)) { sLog.outErrorDb("`pool_gameobject` has guid %u for pool %u that already added to pool %u from `pool_gameobject_template` for gameobject entry %u, skipped.", guid, pool_id, alt_pool_id, entry_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_gameobject_template", "gameobject guid")) continue; PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject_template`", count); delete result; } // Pool of pools mPoolPoolGroups.resize(max_pool_id + 1); // 1 2 3 result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u pools in pools", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint16 child_pool_id = fields[0].GetUInt16(); uint16 mother_pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); if (mother_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", mother_pool_id); continue; } if (child_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.", child_pool_id); continue; } if (mother_pool_id == child_pool_id) { sLog.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.", child_pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%i), skipped.", chance, child_pool_id, mother_pool_id); continue; } PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id]; ++count; PoolObject plObject = PoolObject(child_pool_id, chance); PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id]; plgroup.SetPoolId(mother_pool_id); plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit); SearchPair p(child_pool_id, mother_pool_id); mPoolSearchMap.insert(p); // update top independent pool flag mPoolTemplate[child_pool_id].AutoSpawn = false; } while (result->NextRow()); // Now check for circular reference for (uint16 i = 0; i < max_pool_id; ++i) { std::set<uint16> checkedPools; for (SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second)) { // if child pool not have map data then it empty or have not checked child then will checked and all line later if (MapEntry const* childMapEntry = mPoolTemplate[poolItr->first].mapEntry) { if (!mapChecker.CheckAndRemember(childMapEntry->MapID, poolItr->second, "pool_pool", "pool with creature/gameobject")) { mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } checkedPools.insert(poolItr->first); if (checkedPools.find(poolItr->second) != checkedPools.end()) { std::ostringstream ss; ss << "The pool(s) "; for (std::set<uint16>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr) ss << *itr << " "; ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool " << poolItr->first << " and child pool " << poolItr->second; sLog.outErrorDb("%s", ss.str().c_str()); mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } } sLog.outString(); sLog.outString(">> Loaded %u pools in mother pools", count); delete result; } // check chances integrity for (uint16 pool_entry = 0; pool_entry < mPoolTemplate.size(); ++pool_entry) { if (mPoolTemplate[pool_entry].AutoSpawn) { if (!CheckPool(pool_entry)) { sLog.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", pool_entry); mPoolTemplate[pool_entry].AutoSpawn = false; } } } }
void PoolMgr::LoadFromDB() { // Pool templates { uint32 oldMSTime = getMSTime(); QueryResult result = WorldDatabase.Query("SELECT entry, max_limit FROM pool_template"); if (!result) { mPoolTemplate.clear(); sLog->outString(">> Loaded 0 object pools. DB table `pool_template` is empty."); sLog->outString(); return; } uint32 count = 0; do { Field* fields = result->Fetch(); uint32 pool_id = fields[0].GetUInt32(); PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id]; pPoolTemplate.MaxLimit = fields[1].GetUInt32(); ++count; } while (result->NextRow()); sLog->outString(">> Loaded %u objects pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } // Creatures sLog->outString("Loading Creatures Pooling Data..."); { uint32 oldMSTime = getMSTime(); // 1 2 3 QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); if (!result) { sLog->outString(">> Loaded 0 creatures in pools. DB table `pool_creature` is empty."); sLog->outString(); } else { uint32 count = 0; do { Field* fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); uint32 pool_id = fields[1].GetUInt32(); float chance = fields[2].GetFloat(); CreatureData const* data = sObjectMgr->GetCreatureData(guid); if (!data) { sLog->outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } if (pool_id > max_pool_id) { sLog->outErrorDb("`pool_creature` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog->outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%u), skipped.", chance, guid, pool_id); continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); ++count; } while (result->NextRow()); sLog->outString(">> Loaded %u creatures in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } } // Gameobjects sLog->outString("Loading Gameobject Pooling Data..."); { uint32 oldMSTime = getMSTime(); // 1 2 3 QueryResult result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); if (!result) { sLog->outString(">> Loaded 0 gameobjects in pools. DB table `pool_gameobject` is empty."); sLog->outString(); } else { uint32 count = 0; do { Field* fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); uint32 pool_id = fields[1].GetUInt32(); float chance = fields[2].GetFloat(); GameObjectData const* data = sObjectMgr->GetGOData(guid); if (!data) { sLog->outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(data->id); if (goinfo->type != GAMEOBJECT_TYPE_CHEST && goinfo->type != GAMEOBJECT_TYPE_GOOBER && goinfo->type != GAMEOBJECT_TYPE_FISHINGHOLE) { sLog->outErrorDb("`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid, goinfo->type, pool_id); continue; } if (pool_id > max_pool_id) { sLog->outErrorDb("`pool_gameobject` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog->outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%u), skipped.", chance, guid, pool_id); continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); ++count; } while (result->NextRow()); sLog->outString(">> Loaded %u gameobject in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } } // Pool of pools sLog->outString("Loading Mother Pooling Data..."); { uint32 oldMSTime = getMSTime(); // 1 2 3 QueryResult result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); if (!result) { sLog->outString(">> Loaded 0 pools in pools"); sLog->outString(); } else { uint32 count = 0; do { Field* fields = result->Fetch(); uint32 child_pool_id = fields[0].GetUInt32(); uint32 mother_pool_id = fields[1].GetUInt32(); float chance = fields[2].GetFloat(); if (mother_pool_id > max_pool_id) { sLog->outErrorDb("`pool_pool` mother_pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", mother_pool_id); continue; } if (child_pool_id > max_pool_id) { sLog->outErrorDb("`pool_pool` included pool_id (%u) is out of range compared to max pool id in `pool_template`, skipped.", child_pool_id); continue; } if (mother_pool_id == child_pool_id) { sLog->outErrorDb("`pool_pool` pool_id (%u) includes itself, dead-lock detected, skipped.", child_pool_id); continue; } if (chance < 0 || chance > 100) { sLog->outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%u), skipped.", chance, child_pool_id, mother_pool_id); continue; } PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id]; PoolObject plObject = PoolObject(child_pool_id, chance); PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id]; plgroup.SetPoolId(mother_pool_id); plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit); SearchPair p(child_pool_id, mother_pool_id); mPoolSearchMap.insert(p); ++count; } while (result->NextRow()); // Now check for circular reference for (uint32 i=0; i < max_pool_id; ++i) { std::set<uint32> checkedPools; for (SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second)) { checkedPools.insert(poolItr->first); if (checkedPools.find(poolItr->second) != checkedPools.end()) { std::ostringstream ss; ss<< "The pool(s) "; for (std::set<uint32>::const_iterator itr=checkedPools.begin(); itr != checkedPools.end(); ++itr) ss << *itr << ' '; ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool " << poolItr->first << " and child pool " << poolItr->second; sLog->outErrorDb("%s", ss.str().c_str()); mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } } sLog->outString(">> Loaded %u pools in mother pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } } sLog->outString("Loading Quest Pooling Data..."); { uint32 oldMSTime = getMSTime(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_LOAD_QUEST_POOLS); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) { sLog->outString(">> Loaded 0 quests in pools"); sLog->outString(); } else { PooledQuestRelationBounds creBounds; PooledQuestRelationBounds goBounds; enum eQuestTypes { QUEST_NONE = 0, QUEST_DAILY = 1, QUEST_WEEKLY = 2 }; std::map<uint32, int32> poolTypeMap; uint32 count = 0; do { Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 pool_id = fields[1].GetUInt32(); Quest const* quest = sObjectMgr->GetQuestTemplate(entry); if (!quest) { sLog->outErrorDb("`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id); continue; } if (pool_id > max_pool_id) { sLog->outErrorDb("`pool_quest` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (!quest->IsDailyOrWeekly()) { sLog->outErrorDb("`pool_quest` has an quest (%u) which is not daily or weekly in pool id (%u), use ExclusiveGroup instead, skipped.", entry, pool_id); continue; } if (poolTypeMap[pool_id] == QUEST_NONE) poolTypeMap[pool_id] = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; int32 currType = quest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; if (poolTypeMap[pool_id] != currType) { sLog->outErrorDb("`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.", entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY"); continue; } creBounds = mQuestCreatureRelation.equal_range(entry); goBounds = mQuestGORelation.equal_range(entry); if (creBounds.first == creBounds.second && goBounds.first == goBounds.second) { sLog->outErrorDb("`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id); continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; PoolObject plObject = PoolObject(entry, 0.0f); PoolGroup<Quest>& questgroup = mPoolQuestGroups[pool_id]; questgroup.SetPoolId(pool_id); questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(entry, pool_id); mQuestSearchMap.insert(p); ++count; } while (result->NextRow()); sLog->outString(">> Loaded %u quests in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } } // 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 sLog->outString("Starting objects pooling system..."); { uint32 oldMSTime = getMSTime(); QueryResult result = WorldDatabase.Query("SELECT DISTINCT pool_template.entry, pool_pool.pool_id, pool_pool.mother_pool FROM pool_template" " LEFT JOIN game_event_pool ON pool_template.entry=game_event_pool.pool_entry" " LEFT JOIN pool_pool ON pool_template.entry=pool_pool.pool_id WHERE game_event_pool.pool_entry IS NULL"); if (!result) { sLog->outString(">> Pool handling system initialized, 0 pools spawned."); sLog->outString(); } else { uint32 count = 0; do { Field* fields = result->Fetch(); uint32 pool_entry = fields[0].GetUInt32(); uint32 pool_pool_id = fields[1].GetUInt32(); if (!CheckPool(pool_entry)) { if (pool_pool_id) // 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, // however that could recursively invalidate entire chain of mother pools. It can be done in the future but for now we'll do nothing. sLog->outErrorDb("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()); else sLog->outErrorDb("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); continue; } // Don't spawn child pools, they are spawned recursively by their parent pools if (!pool_pool_id) { SpawnPool(pool_entry); count++; } } while (result->NextRow()); sLog->outBasic("Pool handling system initialized, %u pools spawned in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } } }
void PoolManager::LoadFromDB() { QueryResult* result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template"); if (!result) { sLog.outString(">> Table pool_template is empty."); sLog.outString(); return; } else { Field* fields = result->Fetch(); max_pool_id = fields[0].GetUInt16(); delete result; } mPoolTemplate.resize(max_pool_id + 1); result = WorldDatabase.Query("SELECT entry, max_limit, description FROM pool_template"); if (!result) { mPoolTemplate.clear(); sLog.outString(">> Table pool_template is empty:"); sLog.outString(); return; } uint32 count = 0; BarGoLink bar(result->GetRowCount()); do { ++count; Field* fields = result->Fetch(); bar.step(); uint16 pool_id = fields[0].GetUInt16(); PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id]; pPoolTemplate.MaxLimit = fields[1].GetUInt32(); pPoolTemplate.description = fields[2].GetCppString(); pPoolTemplate.AutoSpawn = true; // will update and later data loading } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u objects pools", count); delete result; PoolMapChecker mapChecker(mPoolTemplate); // Creatures (guids and entries) mPoolCreatureGroups.resize(max_pool_id + 1); mCreatureSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); CreatureData const* data = sObjectMgr.GetCreatureData(guid); if (!data) { sLog.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_creature", "creature guid", guid)) { continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature`", count); delete result; } result = WorldDatabase.Query("SELECT guid, pool_entry, chance, pool_creature_template.id FROM pool_creature_template LEFT JOIN creature ON creature.id = pool_creature_template.id"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature_template`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); uint16 entry_id = fields[3].GetUInt32(); // for errors output only CreatureData const* data = sObjectMgr.GetCreatureData(guid); if (!data) { sLog.outErrorDb("`pool_creature_template` has a non existing creature spawn (GUID: %u Entry: %u) defined for pool id (%u), skipped.", guid, entry_id, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_creature_template` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_creature_template` has an invalid chance (%f) for creature (Guid %u Entry %u) in pool id (%i), skipped.", chance, guid, entry_id, pool_id); continue; } // `pool_creature` and `pool_creature_template` can't have guids duplicates (in second case because entries also unique) // So if guid already listed in pools then this duplicate from alt.table // Also note: for added guid not important what case we skip from 2 tables if (uint16 alt_pool_id = IsPartOfAPool<Creature>(guid)) { sLog.outErrorDb("`pool_creature` has guid %u for pool %u that already added to pool %u from `pool_creature_template` for creature entry %u, skipped.", guid, pool_id, alt_pool_id, entry_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_creature_template", "creature guid", guid)) { continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools from `pool_creature_template`", count); delete result; } // Gameobjects (guids and entries) mPoolGameobjectGroups.resize(max_pool_id + 1); mGameobjectSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); GameObjectData const* data = sObjectMgr.GetGOData(guid); if (!data) { sLog.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_gameobject", "gameobject guid", guid)) { continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject`", count); delete result; } // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance, pool_gameobject_template.id FROM pool_gameobject_template LEFT JOIN gameobject ON gameobject.id = pool_gameobject_template.id"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject_template`", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); uint16 entry_id = fields[3].GetUInt32(); // for errors output only GameObjectData const* data = sObjectMgr.GetGOData(guid); if (!data) { sLog.outErrorDb("`pool_gameobject_template` has a non existing gameobject spawn (GUID: %u Entry %u) defined for pool id (%u), skipped.", guid, entry_id, pool_id); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_gameobject_template` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_gameobject_template` has an invalid chance (%f) for gameobject (Guid %u Entry %u) in pool id (%i), skipped.", chance, guid, entry_id, pool_id); continue; } // `pool_gameobject` and `pool_gameobject_template` can't have guids duplicates (in second case because entries also unique) // So if guid already listed in pools then this duplicate from alt.table // Also note: for added guid not important what case we skip from 2 tables if (uint16 alt_pool_id = IsPartOfAPool<GameObject>(guid)) { sLog.outErrorDb("`pool_gameobject` has guid %u for pool %u that already added to pool %u from `pool_gameobject_template` for gameobject entry %u, skipped.", guid, pool_id, alt_pool_id, entry_id); continue; } if (!mapChecker.CheckAndRemember(data->mapid, pool_id, "pool_gameobject_template", "gameobject guid", guid)) { continue; } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools from `pool_gameobject_template`", count); delete result; } // Pool of pools mPoolPoolGroups.resize(max_pool_id + 1); // 1 2 3 result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); count = 0; if (!result) { BarGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u pools in pools", count); } else { BarGoLink bar2(result->GetRowCount()); do { Field* fields = result->Fetch(); bar2.step(); uint16 child_pool_id = fields[0].GetUInt16(); uint16 mother_pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); if (mother_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.", mother_pool_id); continue; } if (child_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.", child_pool_id); continue; } if (mother_pool_id == child_pool_id) { sLog.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.", child_pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%i), skipped.", chance, child_pool_id, mother_pool_id); continue; } PoolTemplateData* pPoolTemplateMother = &mPoolTemplate[mother_pool_id]; ++count; PoolObject plObject = PoolObject(child_pool_id, chance); PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id]; plgroup.SetPoolId(mother_pool_id); plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit); SearchPair p(child_pool_id, mother_pool_id); mPoolSearchMap.insert(p); // update top independent pool flag mPoolTemplate[child_pool_id].AutoSpawn = false; } while (result->NextRow()); // Now check for circular reference for (uint16 i = 0; i < max_pool_id; ++i) { std::set<uint16> checkedPools; for (SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second)) { // if child pool not have map data then it empty or have not checked child then will checked and all line later if (MapEntry const* childMapEntry = mPoolTemplate[poolItr->first].mapEntry) { if (!mapChecker.CheckAndRemember(childMapEntry->MapID, poolItr->second, "pool_pool", "pool with creature/gameobject", poolItr->first)) { mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } checkedPools.insert(poolItr->first); if (checkedPools.find(poolItr->second) != checkedPools.end()) { std::ostringstream ss; ss << "The pool(s) "; for (std::set<uint16>::const_iterator itr = checkedPools.begin(); itr != checkedPools.end(); ++itr) { ss << *itr << " "; } ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool " << poolItr->first << " and child pool " << poolItr->second; sLog.outErrorDb("%s", ss.str().c_str()); mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } } sLog.outString(); sLog.outString(">> Loaded %u pools in mother pools", count); delete result; } // check chances integrity for (uint16 pool_entry = 0; pool_entry < mPoolTemplate.size(); ++pool_entry) { if (mPoolTemplate[pool_entry].AutoSpawn) { if (!CheckPool(pool_entry)) { sLog.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system can not pick one to spawn.", pool_entry); mPoolTemplate[pool_entry].AutoSpawn = false; } } } /*if (sWorld.getConfig(CONFIG_BOOL_AUTOPOOLING_MINING_ENABLE)) { // autocreate mining pools mPoolTemplate.resize(max_pool_id + 3486 + 533); //values are hardcoded for max zoneID and max MapID it would be better to read max IDs from the .dbc files mPoolCreatureGroups.resize(max_pool_id + 3486 + 533); mPoolGameobjectGroups.resize(max_pool_id + 3486 + 533); mPoolPoolGroups.resize(max_pool_id + 3486 + 533); uint32 max_autopool_entry = (max_pool_id + 3486 + 533); for (uint16 pool_entry = max_pool_id + 1; pool_entry < max_autopool_entry; ++pool_entry) { PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_entry]; pPoolTemplate.MaxLimit = 0; std::ostringstream sZone; sZone << "autopool zone " << pool_entry; pPoolTemplate.description = sZone.str(); pPoolTemplate.AutoSpawn = true; // will update and later data loading } count = 0; // 0 1 2 3 4 5 result = WorldDatabase.Query("SELECT gameobject.guid, gameobject.id, map, position_x, position_y, position_z," // 6 7 "pool_gameobject.pool_entry, pool_gameobject_template.pool_entry " "FROM gameobject " "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid " "LEFT OUTER JOIN pool_gameobject_template ON gameobject.id = pool_gameobject_template.id"); if (!result) { BarGoLink bar3(1); bar3.step(); sLog.outString(); sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty."); return; } BarGoLink bar3(result->GetRowCount()); do { Field* fields = result->Fetch(); bar3.step(); uint32 guid = fields[ 0].GetUInt32(); uint32 entry = fields[ 1].GetUInt32(); uint32 map = fields[ 2].GetUInt32(); float posX = fields[ 3].GetFloat(); float posY = fields[ 4].GetFloat(); float posZ = fields[ 5].GetFloat(); int16 GuidPoolId = fields[6].GetInt16(); int16 EntryPoolId = fields[7].GetInt16(); if (GuidPoolId != 0 || EntryPoolId != 0) // if not this is in the pool system already { continue; } GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(entry); if (goinfo->type != GAMEOBJECT_TYPE_CHEST) { continue; } if (goinfo->chest.minSuccessOpens != 0 && goinfo->chest.maxSuccessOpens > goinfo->chest.minSuccessOpens) //in this case it is a mineral vein { uint32 zone_id; uint16 pool_id; if (map == 0 || map == 1) { zone_id = sTerrainMgr.LoadTerrain(map)->GetZoneId(posX, posY, posZ); pool_id = zone_id + max_pool_id; } else { zone_id = map; pool_id = zone_id + max_pool_id + 3486; //3486 zero value for maxzoneID } PoolTemplateData* pPoolTemplate = &mPoolTemplate[pool_id]; PoolObject plObject = PoolObject(guid, 0); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, 0); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); if (!mapChecker.CheckAndRemember(map, pool_id, "pool_gameobject", "gameobject guid", guid)) { continue; } ++count; //sLog.outErrorDb("UPDATE gameobject SET zone_id=%u, area_id=%u WHERE guid=%u;", zoneId, areaId, guid); } } while (result->NextRow()); for (uint16 pool_entry = max_pool_id + 1; pool_entry < max_autopool_entry; ++pool_entry) { uint32 poolsize; PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_entry]; PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_entry]; poolsize = gogroup.size(); pPoolTemplate.MaxLimit = (poolsize * CONFIG_UINT32_RATE_MINING_AUTOPOOLING) / 100; } delete result; sLog.outString(">> Loaded %u mining nodes", count); }*/ }
void PoolManager::LoadFromDB() { QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template"); if (!result) { sLog.outString(">> Table pool_template is empty."); sLog.outString(); return; } else { Field *fields = result->Fetch(); max_pool_id = fields[0].GetUInt16(); delete result; } mPoolTemplate.resize(max_pool_id + 1); result = WorldDatabase.Query("SELECT entry,max_limit FROM pool_template"); if (!result) { mPoolTemplate.clear(); sLog.outString(">> Table pool_template is empty:"); sLog.outString(); return; } uint32 count = 0; barGoLink bar((int)result->GetRowCount()); do { ++count; Field *fields = result->Fetch(); bar.step(); uint16 pool_id = fields[0].GetUInt16(); PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id]; pPoolTemplate.MaxLimit = fields[1].GetUInt32(); pPoolTemplate.AutoSpawn = true; // will update and later data loading } while (result->NextRow()); sLog.outString(); sLog.outString( ">> Loaded %u objects pools", count ); delete result; // Creatures mPoolCreatureGroups.resize(max_pool_id + 1); mCreatureSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature"); count = 0; if (!result) { barGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u creatures in pools", count ); } else { barGoLink bar2((int)result->GetRowCount()); do { Field *fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); CreatureData const* data = sObjectMgr.GetCreatureData(guid); if (!data) { sLog.outErrorDb("`pool_creature` has a non existing creature spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id ); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_creature` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_creature` has an invalid chance (%f) for creature guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id]; cregroup.SetPoolId(pool_id); cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mCreatureSearchMap.insert(p); } while (result->NextRow()); sLog.outString(); sLog.outString( ">> Loaded %u creatures in pools", count ); delete result; } // Gameobjects mPoolGameobjectGroups.resize(max_pool_id + 1); mGameobjectSearchMap.clear(); // 1 2 3 result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject"); count = 0; if (!result) { barGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u gameobject in pools", count ); } else { barGoLink bar2((int)result->GetRowCount()); do { Field *fields = result->Fetch(); bar2.step(); uint32 guid = fields[0].GetUInt32(); uint16 pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); GameObjectData const* data = sObjectMgr.GetGOData(guid); if (!data) { sLog.outErrorDb("`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id ); continue; } GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(data->id); if (goinfo->type != GAMEOBJECT_TYPE_CHEST && goinfo->type != GAMEOBJECT_TYPE_GOOBER && goinfo->type != GAMEOBJECT_TYPE_FISHINGHOLE) { sLog.outErrorDb("`pool_gameobject` has a not lootable gameobject spawn (GUID: %u, type: %u) defined for pool id (%u), skipped.", guid, goinfo->type, pool_id ); continue; } if (pool_id > max_pool_id) { sLog.outErrorDb("`pool_gameobject` pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_gameobject` has an invalid chance (%f) for gameobject guid (%u) in pool id (%i), skipped.", chance, guid, pool_id); continue; } PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(guid, chance); PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id]; gogroup.SetPoolId(pool_id); gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(guid, pool_id); mGameobjectSearchMap.insert(p); } while( result->NextRow() ); sLog.outString(); sLog.outString( ">> Loaded %u gameobject in pools", count ); delete result; } // Pool of pools mPoolPoolGroups.resize(max_pool_id + 1); // 1 2 3 result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool"); count = 0; if( !result ) { barGoLink bar2(1); bar2.step(); sLog.outString(); sLog.outString(">> Loaded %u pools in pools", count ); } else { barGoLink bar2( (int)result->GetRowCount() ); do { Field *fields = result->Fetch(); bar2.step(); uint16 child_pool_id = fields[0].GetUInt16(); uint16 mother_pool_id = fields[1].GetUInt16(); float chance = fields[2].GetFloat(); if (mother_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",mother_pool_id); continue; } if (child_pool_id > max_pool_id) { sLog.outErrorDb("`pool_pool` included pool_id (%i) is out of range compared to max pool id in `pool_template`, skipped.",child_pool_id); continue; } if (mother_pool_id == child_pool_id) { sLog.outErrorDb("`pool_pool` pool_id (%i) includes itself, dead-lock detected, skipped.",child_pool_id); continue; } if (chance < 0 || chance > 100) { sLog.outErrorDb("`pool_pool` has an invalid chance (%f) for pool id (%u) in mother pool id (%i), skipped.", chance, child_pool_id, mother_pool_id); continue; } PoolTemplateData *pPoolTemplateMother = &mPoolTemplate[mother_pool_id]; ++count; PoolObject plObject = PoolObject(child_pool_id, chance); PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id]; plgroup.SetPoolId(mother_pool_id); plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit); SearchPair p(child_pool_id, mother_pool_id); mPoolSearchMap.insert(p); // update top independent pool flag mPoolTemplate[child_pool_id].AutoSpawn = false; } while( result->NextRow() ); // Now check for circular reference for(uint16 i=0; i<max_pool_id; ++i) { std::set<uint16> checkedPools; for(SearchMap::iterator poolItr = mPoolSearchMap.find(i); poolItr != mPoolSearchMap.end(); poolItr = mPoolSearchMap.find(poolItr->second)) { checkedPools.insert(poolItr->first); if(checkedPools.find(poolItr->second) != checkedPools.end()) { std::ostringstream ss; ss<< "The pool(s) "; for (std::set<uint16>::const_iterator itr=checkedPools.begin(); itr!=checkedPools.end(); ++itr) ss << *itr << " "; ss << "create(s) a circular reference, which can cause the server to freeze.\nRemoving the last link between mother pool " << poolItr->first << " and child pool " << poolItr->second; sLog.outErrorDb("%s", ss.str().c_str()); mPoolPoolGroups[poolItr->second].RemoveOneRelation(poolItr->first); mPoolSearchMap.erase(poolItr); --count; break; } } } sLog.outString(); sLog.outString( ">> Loaded %u pools in mother pools", count ); delete result; } }
void PoolMgr::LoadQuestPools () { uint32 oldMSTime = getMSTime(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_LOAD_QUEST_POOLS); PreparedQueryResult result = WorldDatabase.Query(stmt); mQuestSearchMap.clear(); mPoolQuestGroups.resize(max_pool_id + 1); uint32 count = 0; if (!result) { sLog->outString(">> Loaded 0 quests in pools"); sLog->outString(); return; } PooledQuestRelationBounds creBounds; PooledQuestRelationBounds goBounds; enum eQuestTypes { QUEST_NONE = 0, QUEST_DAILY = 1, QUEST_WEEKLY = 2 }; std::map<uint32, int32> poolTypeMap; do { Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 pool_id = fields[1].GetUInt32(); Quest const* pQuest = sObjectMgr->GetQuestTemplate(entry); if (!pQuest) { sLog->outErrorDb("`pool_quest` has a non existing quest template (Entry: %u) defined for pool id (%u), skipped.", entry, pool_id); continue; } if (pool_id > max_pool_id) { sLog->outErrorDb("`pool_quest` pool id (%u) is out of range compared to max pool id in `pool_template`, skipped.", pool_id); continue; } if (!pQuest->IsDailyOrWeekly()) { sLog->outErrorDb("`pool_quest` has an quest (%u) which is not daily or weekly in pool id (%u), use ExclusiveGroup instead, skipped.", entry, pool_id); continue; } if (poolTypeMap[pool_id] == QUEST_NONE) poolTypeMap[pool_id] = pQuest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; int32 currType = pQuest->IsDaily() ? QUEST_DAILY : QUEST_WEEKLY; if (poolTypeMap[pool_id] != currType) { sLog->outErrorDb("`pool_quest` quest %u is %s but pool (%u) is specified for %s, mixing not allowed, skipped.", entry, currType == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY", pool_id, poolTypeMap[pool_id] == QUEST_DAILY ? "QUEST_DAILY" : "QUEST_WEEKLY"); continue; } creBounds = mQuestCreatureRelation.equal_range(entry); goBounds = mQuestGORelation.equal_range(entry); if (creBounds.first == creBounds.second && goBounds.first == goBounds.second) { sLog->outErrorDb("`pool_quest` lists entry (%u) as member of pool (%u) but is not started anywhere, skipped.", entry, pool_id); continue; } PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id]; ++count; PoolObject plObject = PoolObject(entry, 0.0f); PoolGroup<Quest>& questgroup = mPoolQuestGroups[pool_id]; questgroup.SetPoolId(pool_id); questgroup.AddEntry(plObject, pPoolTemplate->MaxLimit); SearchPair p(entry, pool_id); mQuestSearchMap.insert(p); } while (result->NextRow()); sLog->outString(">> Loaded %u quests in pools in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); }