TrinityCore
Loading...
Searching...
No Matches
ConversationDataStore.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
19#include "DB2Stores.h"
20#include "DatabaseEnv.h"
21#include "Log.h"
22#include "MapUtils.h"
23#include "ObjectMgr.h"
24#include "Timer.h"
25
26namespace
27{
28 std::unordered_map<uint32, ConversationTemplate> _conversationTemplateStore;
29 std::unordered_map<uint32, ConversationLineTemplate> _conversationLineTemplateStore;
30}
31
33{
34 _conversationLineTemplateStore.clear();
35 _conversationTemplateStore.clear();
36
37 std::unordered_map<uint32, std::vector<ConversationActorTemplate>> actorsByConversation;
38
39 if (QueryResult lineTemplates = WorldDatabase.Query("SELECT Id, UiCameraID, ActorIdx, Flags, ChatType FROM conversation_line_template"))
40 {
41 uint32 oldMSTime = getMSTime();
42
43 do
44 {
45 Field* fields = lineTemplates->Fetch();
46
47 uint32 id = fields[0].GetUInt32();
48
49 if (!sConversationLineStore.LookupEntry(id))
50 {
51 TC_LOG_ERROR("sql.sql", "Table `conversation_line_template` has template for non existing ConversationLine (ID: {}), skipped", id);
52 continue;
53 }
54
55 ConversationLineTemplate& conversationLine = _conversationLineTemplateStore[id];
56 conversationLine.Id = id;
57 conversationLine.UiCameraID = fields[1].GetUInt32();
58 conversationLine.ActorIdx = fields[2].GetUInt8();
59 conversationLine.Flags = fields[3].GetUInt8();
60 conversationLine.ChatType = fields[4].GetUInt8();
61 }
62 while (lineTemplates->NextRow());
63
64 TC_LOG_INFO("server.loading", ">> Loaded {} Conversation line templates in {} ms", _conversationLineTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
65 }
66 else
67 {
68 TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation line templates. DB table `conversation_line_template` is empty.");
69 }
70
71 if (QueryResult actors = WorldDatabase.Query("SELECT ConversationId, ConversationActorId, ConversationActorGuid, Idx, CreatureId, CreatureDisplayInfoId, NoActorObject, ActivePlayerObject FROM conversation_actors"))
72 {
73 uint32 oldMSTime = getMSTime();
74 uint32 count = 0;
75
76 struct ConversationActorDbRow
77 {
78 uint32 ConversationId = 0;
79 uint32 ActorIndex = 0;
80
81 ObjectGuid::LowType SpawnId = 0;
83 uint32 CreatureDisplayInfoId = 0;
84
85 bool operator()(ConversationActorWorldObjectTemplate& worldObject) const
86 {
87 if (!sObjectMgr->GetCreatureData(SpawnId))
88 {
89 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature guid (GUID: {}) for Conversation {} and Idx {}, skipped.", SpawnId, ConversationId, ActorIndex);
90 return false;
91 }
92
93 if (CreatureId)
94 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ConversationActorGuid cannot have CreatureId ({}). Conversation {} and Idx {}.", CreatureId, ConversationId, ActorIndex);
95
96 if (CreatureDisplayInfoId)
97 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ConversationActorGuid cannot have CreatureDisplayInfoId ({}). Conversation {} and Idx {}.", CreatureDisplayInfoId, ConversationId, ActorIndex);
98
99 worldObject.SpawnId = SpawnId;
100 return true;
101 }
102
103 bool operator()(ConversationActorNoObjectTemplate& noObject) const
104 {
105 if (!sObjectMgr->GetCreatureTemplate(CreatureId))
106 {
107 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature id ({}) for Conversation {} and Idx {}, skipped.", CreatureId, ConversationId, ActorIndex);
108 return false;
109 }
110
111 if (CreatureDisplayInfoId && !sCreatureDisplayInfoStore.LookupEntry(CreatureDisplayInfoId))
112 {
113 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature display id ({}) for Conversation {} and Idx {}, skipped.", CreatureDisplayInfoId, ConversationId, ActorIndex);
114 return false;
115 }
116
117 if (SpawnId)
118 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with NoActorObject cannot have ConversationActorGuid ({}). Conversation {} and Idx {}.", SpawnId, ConversationId, ActorIndex);
119
120 noObject.CreatureId = CreatureId;
121 noObject.CreatureDisplayInfoId = CreatureDisplayInfoId;
122 return true;
123 }
124
125 bool operator()([[maybe_unused]] ConversationActorActivePlayerTemplate& activePlayer) const
126 {
127 if (SpawnId)
128 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have ConversationActorGuid ({}). Conversation {} and Idx {}.", SpawnId, ConversationId, ActorIndex);
129
130 if (CreatureId)
131 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have CreatureId ({}). Conversation {} and Idx {}.", CreatureId, ConversationId, ActorIndex);
132
133 if (CreatureDisplayInfoId)
134 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have CreatureDisplayInfoId ({}). Conversation {} and Idx {}.", CreatureDisplayInfoId, ConversationId, ActorIndex);
135
136 return true;
137 }
138
139 bool operator()(ConversationActorTalkingHeadTemplate& talkingHead) const
140 {
141 if (!sObjectMgr->GetCreatureTemplate(CreatureId))
142 {
143 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature id ({}) for Conversation {} and Idx {}, skipped.", CreatureId, ConversationId, ActorIndex);
144 return false;
145 }
146
147 if (CreatureDisplayInfoId && !sCreatureDisplayInfoStore.LookupEntry(CreatureDisplayInfoId))
148 {
149 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature display id ({}) for Conversation {} and Idx {}, skipped.", CreatureDisplayInfoId, ConversationId, ActorIndex);
150 return false;
151 }
152
153 if (SpawnId)
154 TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with TalkingHead cannot have ConversationActorGuid ({}). Conversation {} and Idx {}.", SpawnId, ConversationId, ActorIndex);
155
156 talkingHead.CreatureId = CreatureId;
157 talkingHead.CreatureDisplayInfoId = CreatureDisplayInfoId;
158 return true;
159 }
160 };
161
162 do
163 {
164 Field* fields = actors->Fetch();
165
166 ConversationActorDbRow data;
168 data.ConversationId = fields[0].GetUInt32();
169 actor.Id = fields[1].GetUInt32();
170 data.SpawnId = fields[2].GetUInt64();
171 data.ActorIndex = actor.Index = fields[3].GetUInt16();
172 data.CreatureId = fields[4].GetUInt32();
173 data.CreatureDisplayInfoId = fields[5].GetUInt32();
174 bool noActorObject = fields[6].GetUInt8() == 1;
175 bool activePlayerObject = fields[7].GetUInt8() == 1;
176
177 if (activePlayerObject)
179 else if (noActorObject)
181 else if (data.SpawnId || !data.CreatureId) // @TODO: remove CreatureId check when actor flags are implemented
183 else
185
186 bool valid = std::visit(data, actor.Data);
187 if (!valid)
188 continue;
189
190 actorsByConversation[data.ConversationId].push_back(actor);
191 ++count;
192 } while (actors->NextRow());
193
194 TC_LOG_INFO("server.loading", ">> Loaded {} Conversation actors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
195 }
196 else
197 {
198 TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation actors. DB table `conversation_actors` is empty.");
199 }
200
201 // TODO: Remove this hack when NextConversationLineID is changed to uint32
202 auto getNextConversationLineId = [&](ConversationLineEntry const* conversationLine)
203 {
204 if (conversationLine && conversationLine->NextConversationLineID)
205 {
206 static constexpr uint32 FirstLineId = 60000; // Arbitrary id to cover the affected rows
207
208 if (conversationLine->ID > FirstLineId && conversationLine->NextConversationLineID < (sConversationLineStore.GetNumRows() - USHRT_MAX - 1))
209 return (uint32)(USHRT_MAX + conversationLine->NextConversationLineID + 1);
210
211 return (uint32)conversationLine->NextConversationLineID;
212 }
213
214 return 0u;
215 };
216
217 // Validate FirstLineId
218 std::unordered_map<uint32, uint32> prevConversationLineIds;
219 for (ConversationLineEntry const* conversationLine : sConversationLineStore)
220 if (uint32 nextConversationLineId = getNextConversationLineId(conversationLine))
221 prevConversationLineIds[nextConversationLineId] = conversationLine->ID;
222
223 auto getFirstLineIdFromAnyLineId = [&](uint32 lineId)
224 {
225 while (uint32 const* prevLineId = Trinity::Containers::MapGetValuePtr(prevConversationLineIds, lineId))
226 lineId = *prevLineId;
227
228 return lineId;
229 };
230
231 if (QueryResult templates = WorldDatabase.Query("SELECT Id, FirstLineId, TextureKitId, Flags, ScriptName FROM conversation_template"))
232 {
233 uint32 oldMSTime = getMSTime();
234
235 do
236 {
237 Field* fields = templates->Fetch();
238
239 ConversationTemplate conversationTemplate;
240 conversationTemplate.Id = fields[0].GetUInt32();
241 conversationTemplate.FirstLineId = fields[1].GetUInt32();
242 conversationTemplate.TextureKitId = fields[2].GetUInt32();
243 conversationTemplate.Flags = (ConversationFlags)fields[3].GetUInt8();
244 conversationTemplate.ScriptId = sObjectMgr->GetScriptId(fields[4].GetStringView());
245
246 conversationTemplate.Actors = std::move(actorsByConversation[conversationTemplate.Id]);
247
248 uint32 correctedFirstLineId = getFirstLineIdFromAnyLineId(conversationTemplate.FirstLineId);
249 if (conversationTemplate.FirstLineId != correctedFirstLineId)
250 {
251 TC_LOG_ERROR("sql.sql", "Table `conversation_template` has incorrect FirstLineId {}, it should be {} for Conversation {}, corrected",
252 conversationTemplate.FirstLineId, correctedFirstLineId, conversationTemplate.Id);
253 conversationTemplate.FirstLineId = correctedFirstLineId;
254 }
255
256 ConversationLineEntry const* currentConversationLine = sConversationLineStore.LookupEntry(conversationTemplate.FirstLineId);
257 if (!currentConversationLine)
258 TC_LOG_ERROR("sql.sql", "Table `conversation_template` references an invalid line (ID: {}) for Conversation {}, skipped", conversationTemplate.FirstLineId, conversationTemplate.Id);
259
260 while (currentConversationLine != nullptr)
261 {
262 if (ConversationLineTemplate const* conversationLineTemplate = Trinity::Containers::MapGetValuePtr(_conversationLineTemplateStore, currentConversationLine->ID))
263 conversationTemplate.Lines.push_back(conversationLineTemplate);
264 else
265 TC_LOG_ERROR("sql.sql", "Table `conversation_line_template` has missing template for line (ID: {}) in Conversation {}, skipped", currentConversationLine->ID, conversationTemplate.Id);
266
267 uint32 nextConversationLineId = getNextConversationLineId(currentConversationLine);
268 if (!nextConversationLineId)
269 break;
270
271 currentConversationLine = sConversationLineStore.AssertEntry(nextConversationLineId);
272 }
273
274 _conversationTemplateStore[conversationTemplate.Id] = std::move(conversationTemplate);
275 }
276 while (templates->NextRow());
277
278 TC_LOG_INFO("server.loading", ">> Loaded {} Conversation templates in {} ms", _conversationTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
279 }
280 else
281 {
282 TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation templates. DB table `conversation_template` is empty.");
283 }
284}
285
287{
288 return Trinity::Containers::MapGetValuePtr(_conversationTemplateStore, conversationId);
289}
290
292{
293 return Trinity::Containers::MapGetValuePtr(_conversationLineTemplateStore, conversationLineId);
294}
295
297{
298 static ConversationDataStore instance;
299 return &instance;
300}
DB2Storage< ConversationLineEntry > sConversationLineStore("ConversationLine.db2", &ConversationLineLoadInfo::Instance)
DB2Storage< CreatureDisplayInfoEntry > sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", &CreatureDisplayInfoLoadInfo::Instance)
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
uint32_t uint32
Definition Define.h:154
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
#define sObjectMgr
Definition ObjectMgr.h:1885
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
static ConversationDataStore * Instance()
ConversationLineTemplate const * GetConversationLineTemplate(uint32 conversationLineId) const
ConversationTemplate const * GetConversationTemplate(uint32 conversationId) const
Class used to access individual fields of database query result.
Definition Field.h:94
uint64 GetUInt64() const noexcept
Definition Field.cpp:71
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
uint16 GetUInt16() const noexcept
Definition Field.cpp:43
uint8 GetUInt8() const noexcept
Definition Field.cpp:29
uint64 LowType
Definition ObjectGuid.h:321
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
std::variant< ConversationActorWorldObjectTemplate, ConversationActorNoObjectTemplate, ConversationActorActivePlayerTemplate, ConversationActorTalkingHeadTemplate > Data
EnumFlag< ConversationFlags > Flags
std::vector< ConversationLineTemplate const * > Lines
std::vector< ConversationActorTemplate > Actors