TrinityCore
QuestPools.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 "QuestPools.h"
19#include "Containers.h"
20#include "DatabaseEnv.h"
21#include "Log.h"
22#include "ObjectMgr.h"
23#include "QuestDef.h"
24#include "Timer.h"
25#include <unordered_map>
26#include <unordered_set>
27
29{
31 return &instance;
32}
33
34static void RegeneratePool(QuestPool& pool)
35{
36 ASSERT(!pool.members.empty(), "Quest pool %u is empty", pool.poolId);
37 uint32 const n = pool.members.size()-1;
38 ASSERT(pool.numActive <= pool.members.size(), "Quest Pool %u requests %u spawns, but has only %u members.", pool.poolId, pool.numActive, uint32(pool.members.size()));
39 pool.activeQuests.clear();
40 for (uint32 i = 0; i < pool.numActive; ++i)
41 {
42 uint32 j = urand(i,n);
43 if (i != j)
44 std::swap(pool.members[i], pool.members[j]);
45 for (uint32 quest : pool.members[i])
46 pool.activeQuests.insert(quest);
47 }
48}
49
50static void SaveToDB(QuestPool const& pool, CharacterDatabaseTransaction trans)
51{
53 delStmt->setUInt32(0, pool.poolId);
54 trans->Append(delStmt);
55
56 for (uint32 questId : pool.activeQuests)
57 {
59 insStmt->setUInt32(0, pool.poolId);
60 insStmt->setUInt32(1, questId);
61 trans->Append(insStmt);
62 }
63}
64
66{
67 uint32 oldMSTime = getMSTime();
68 std::unordered_map<uint32, std::pair<std::vector<QuestPool>*, uint32>> lookup; // poolId -> (vector, index)
69
70 _poolLookup.clear();
71 _dailyPools.clear();
72 _weeklyPools.clear();
73 _monthlyPools.clear();
74
75 // load template data from world DB
76 {
77 QueryResult result = WorldDatabase.Query("SELECT qpm.questId, qpm.poolId, qpm.poolIndex, qpt.numActive FROM quest_pool_members qpm LEFT JOIN quest_pool_template qpt ON qpm.poolId = qpt.poolId");
78 if (!result)
79 {
80 TC_LOG_INFO("server.loading", ">> Loaded 0 quest pools. DB table `quest_pool_members` is empty.");
81 return;
82 }
83
84 do
85 {
86 Field* fields = result->Fetch();
87 if (fields[2].IsNull())
88 {
89 TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to non-existing pool {}. Skipped.", fields[1].GetUInt32());
90 continue;
91 }
92
93 uint32 questId = fields[0].GetUInt32();
94 uint32 poolId = fields[1].GetUInt32();
95 uint32 poolIndex = fields[2].GetUInt8();
96 uint32 numActive = fields[3].GetUInt32();
97
98 Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
99 if (!quest)
100 {
101 TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to non-existing quest {}. Skipped.", questId);
102 continue;
103 }
104 if (!quest->IsDailyOrWeekly() && !quest->IsMonthly())
105 {
106 TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains reference to quest {}, which is neither daily, weekly nor monthly. Skipped.", questId);
107 continue;
108 }
109
110 auto& pair = lookup[poolId];
111 if (!pair.first)
112 {
113 pair.first = (quest->IsDaily() ? &_dailyPools : quest->IsWeekly() ? &_weeklyPools : &_monthlyPools);
114 pair.first->emplace_back();
115 pair.second = (pair.first->size()-1);
116
117 pair.first->back().poolId = poolId;
118 pair.first->back().numActive = numActive;
119 }
120
121 QuestPool::Members& members = (*pair.first)[pair.second].members;
122 if (!(poolIndex < members.size()))
123 members.resize(poolIndex+1);
124 members[poolIndex].push_back(questId);
125 } while (result->NextRow());
126 }
127
128 // load saved spawns from character DB
129 {
130 QueryResult result = CharacterDatabase.Query("SELECT pool_id, quest_id FROM pool_quest_save");
131 if (result)
132 {
133 std::unordered_set<uint32> unknownPoolIds;
134 do
135 {
136 Field* fields = result->Fetch();
137
138 uint32 poolId = fields[0].GetUInt32();
139 uint32 questId = fields[1].GetUInt32();
140
141 auto it = lookup.find(poolId);
142 if (it == lookup.end() || !it->second.first)
143 {
144 TC_LOG_ERROR("sql.sql", "Table `pool_quest_save` contains reference to non-existant quest pool {}. Deleted.", poolId);
145 unknownPoolIds.insert(poolId);
146 continue;
147 }
148
149 (*it->second.first)[it->second.second].activeQuests.insert(questId);
150 } while (result->NextRow());
151
152 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
153 for (uint32 poolId : unknownPoolIds)
154 {
156 stmt->setUInt32(0, poolId);
157 trans->Append(stmt);
158 }
159 CharacterDatabase.CommitTransaction(trans);
160 }
161 }
162
163 // post-processing and sanity checks
164 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
165 for (auto const& pair : lookup)
166 {
167 if (!pair.second.first)
168 continue;
169 QuestPool& pool = (*pair.second.first)[pair.second.second];
170 if (pool.members.size() < pool.numActive)
171 {
172 TC_LOG_ERROR("sql.sql", "Table `quest_pool_template` contains quest pool {} requesting {} spawns, but only has {} members. Requested spawns reduced.", pool.poolId, pool.numActive, pool.members.size());
173 pool.numActive = pool.members.size();
174 }
175
176 bool doRegenerate = pool.activeQuests.empty();
177 if (!doRegenerate)
178 {
179 std::unordered_set<uint32> accountedFor;
180 uint32 activeCount = 0;
181 for (size_t i = pool.members.size(); (i--);)
182 {
183 QuestPool::Member& member = pool.members[i];
184 if (member.empty())
185 {
186 TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` contains no entries at index {} for quest pool {}. Index removed.", i, pool.poolId);
187 std::swap(pool.members[i], pool.members.back());
188 pool.members.pop_back();
189 continue;
190 }
191
192 // check if the first member is active
193 auto itFirst = pool.activeQuests.find(member[0]);
194 bool status = (itFirst != pool.activeQuests.end());
195 // temporarily remove any spawns that are accounted for
196 if (status)
197 {
198 accountedFor.insert(member[0]);
199 pool.activeQuests.erase(itFirst);
200 }
201
202 // now check if all other members also have the same status, and warn if not
203 for (auto it = member.begin(); (++it) != member.end();)
204 {
205 auto itOther = pool.activeQuests.find(*it);
206 bool otherStatus = (itOther != pool.activeQuests.end());
207 if (status != otherStatus)
208 TC_LOG_WARN("sql.sql", "Table `pool_quest_save` {} quest {} (in pool {}, index {}) saved, but its index is{} active (because quest {} is{} in the table). Set quest {} to {}active.", (status ? "does not have" : "has"), *it, pool.poolId, i, (status ? "" : " not"), member[0], (status ? "" : " not"), *it, (status ? "" : "in"));
209 if (otherStatus)
210 pool.activeQuests.erase(itOther);
211 if (status)
212 accountedFor.insert(*it);
213 }
214
215 if (status)
216 ++activeCount;
217 }
218
219 // warn for any remaining active spawns (not part of the pool)
220 for (uint32 quest : pool.activeQuests)
221 TC_LOG_WARN("sql.sql", "Table `pool_quest_save` has saved quest {} for pool {}, but that quest is not part of the pool. Skipped.", quest, pool.poolId);
222
223 // only the previously-found spawns should actually be active
224 std::swap(pool.activeQuests, accountedFor);
225
226 if (activeCount != pool.numActive)
227 {
228 doRegenerate = true;
229 TC_LOG_ERROR("sql.sql", "Table `pool_quest_save` has {} active members saved for pool {}, which requests {} active members. Pool spawns re-generated.", activeCount, pool.poolId, pool.numActive);
230 }
231 }
232
233 if (doRegenerate)
234 {
235 RegeneratePool(pool);
236 SaveToDB(pool, trans);
237 }
238
239 for (QuestPool::Member const& member : pool.members)
240 {
241 for (uint32 quest : member)
242 {
243 QuestPool*& ref = _poolLookup[quest];
244 if (ref)
245 {
246 TC_LOG_ERROR("sql.sql", "Table `quest_pool_members` lists quest {} as member of pool {}, but it is already a member of pool {}. Skipped.", quest, pool.poolId, ref->poolId);
247 continue;
248 }
249 ref = &pool;
250 }
251 }
252 }
253 CharacterDatabase.CommitTransaction(trans);
254
255 TC_LOG_INFO("server.loading", ">> Loaded {} daily, {} weekly and {} monthly quest pools in {} ms", _dailyPools.size(), _weeklyPools.size(), _monthlyPools.size(), GetMSTimeDiffToNow(oldMSTime));
256}
257
258void QuestPoolMgr::Regenerate(std::vector<QuestPool>& pools)
259{
260 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
261 for (QuestPool& pool : pools)
262 {
263 RegeneratePool(pool);
264 SaveToDB(pool, trans);
265 }
266 CharacterDatabase.CommitTransaction(trans);
267}
268
269// the storage structure ends up making this kind of inefficient
270// we don't use it in practice (only in debug commands), so that's fine
272{
273 auto lambda = [poolId](QuestPool const& p) { return (p.poolId == poolId); };
274 auto it = std::find_if(_dailyPools.begin(), _dailyPools.end(), lambda);
275 if (it != _dailyPools.end())
276 return &*it;
277 it = std::find_if(_weeklyPools.begin(), _weeklyPools.end(), lambda);
278 if (it != _weeklyPools.end())
279 return &*it;
280 it = std::find_if(_monthlyPools.begin(), _monthlyPools.end(), lambda);
281 if (it != _monthlyPools.end())
282 return &*it;
283 return nullptr;
284}
285
287{
288 auto it = _poolLookup.find(questId);
289 if (it == _poolLookup.end()) // not pooled
290 return true;
291
292 return (it->second->activeQuests.find(questId) != it->second->activeQuests.end());
293}
@ CHAR_INS_POOL_QUEST_SAVE
@ CHAR_DEL_POOL_QUEST_SAVE
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_WARN(filterType__,...)
Definition: Log.h:162
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define sObjectMgr
Definition: ObjectMgr.h:1946
static void RegeneratePool(QuestPool &pool)
Definition: QuestPools.cpp:34
static void SaveToDB(QuestPool const &pool, CharacterDatabaseTransaction trans)
Definition: QuestPools.cpp:50
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
if(posix_memalign(&__mallocedMemory, __align, __size)) return NULL
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
Class used to access individual fields of database query result.
Definition: Field.h:90
uint8 GetUInt8() const
Definition: Field.cpp:30
uint32 GetUInt32() const
Definition: Field.cpp:62
void setUInt32(const uint8 index, const uint32 value)
std::vector< QuestPool > _dailyPools
Definition: QuestPools.h:57
std::vector< QuestPool > _weeklyPools
Definition: QuestPools.h:58
bool IsQuestActive(uint32 questId) const
Definition: QuestPools.cpp:286
std::unordered_map< uint32, QuestPool * > _poolLookup
Definition: QuestPools.h:60
std::vector< QuestPool > _monthlyPools
Definition: QuestPools.h:59
static QuestPoolMgr * instance()
Definition: QuestPools.cpp:28
void LoadFromDB()
Definition: QuestPools.cpp:65
void Regenerate(std::vector< QuestPool > &pools)
Definition: QuestPools.cpp:258
QuestPool const * FindQuestPool(uint32 poolId) const
Definition: QuestPools.cpp:271
bool IsDaily() const
Definition: QuestDef.h:674
bool IsWeekly() const
Definition: QuestDef.h:675
bool IsDailyOrWeekly() const
Definition: QuestDef.h:678
bool IsMonthly() const
Definition: QuestDef.h:676
std::vector< Member > Members
Definition: QuestPools.h:29
uint32 numActive
Definition: QuestPools.h:32
std::unordered_set< uint32 > activeQuests
Definition: QuestPools.h:34
std::vector< uint32 > Member
Definition: QuestPools.h:28
uint32 poolId
Definition: QuestPools.h:31
Members members
Definition: QuestPools.h:33