TrinityCore
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 "Containers.h"
20#include "DatabaseEnv.h"
21#include "DB2Stores.h"
22#include "Log.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 // Validate FirstLineId
202 std::unordered_map<uint32, uint32> prevConversationLineIds;
203 for (ConversationLineEntry const* conversationLine : sConversationLineStore)
204 if (conversationLine->NextConversationLineID)
205 prevConversationLineIds[conversationLine->NextConversationLineID] = conversationLine->ID;
206
207 auto getFirstLineIdFromAnyLineId = [&](uint32 lineId)
208 {
209 while (uint32 const* prevLineId = Trinity::Containers::MapGetValuePtr(prevConversationLineIds, lineId))
210 lineId = *prevLineId;
211
212 return lineId;
213 };
214
215 if (QueryResult templates = WorldDatabase.Query("SELECT Id, FirstLineId, TextureKitId, Flags, ScriptName FROM conversation_template"))
216 {
217 uint32 oldMSTime = getMSTime();
218
219 do
220 {
221 Field* fields = templates->Fetch();
222
223 ConversationTemplate conversationTemplate;
224 conversationTemplate.Id = fields[0].GetUInt32();
225 conversationTemplate.FirstLineId = fields[1].GetUInt32();
226 conversationTemplate.TextureKitId = fields[2].GetUInt32();
227 conversationTemplate.Flags = (ConversationFlags)fields[3].GetUInt8();
228 conversationTemplate.ScriptId = sObjectMgr->GetScriptId(fields[4].GetString());
229
230 conversationTemplate.Actors = std::move(actorsByConversation[conversationTemplate.Id]);
231
232 uint32 correctedFirstLineId = getFirstLineIdFromAnyLineId(conversationTemplate.FirstLineId);
233 if (conversationTemplate.FirstLineId != correctedFirstLineId)
234 {
235 TC_LOG_ERROR("sql.sql", "Table `conversation_template` has incorrect FirstLineId {}, it should be {} for Conversation {}, corrected",
236 conversationTemplate.FirstLineId, correctedFirstLineId, conversationTemplate.Id);
237 conversationTemplate.FirstLineId = correctedFirstLineId;
238 }
239
240 ConversationLineEntry const* currentConversationLine = sConversationLineStore.LookupEntry(conversationTemplate.FirstLineId);
241 if (!currentConversationLine)
242 TC_LOG_ERROR("sql.sql", "Table `conversation_template` references an invalid line (ID: {}) for Conversation {}, skipped", conversationTemplate.FirstLineId, conversationTemplate.Id);
243
244 while (currentConversationLine != nullptr)
245 {
246 if (ConversationLineTemplate const* conversationLineTemplate = Trinity::Containers::MapGetValuePtr(_conversationLineTemplateStore, currentConversationLine->ID))
247 conversationTemplate.Lines.push_back(conversationLineTemplate);
248 else
249 TC_LOG_ERROR("sql.sql", "Table `conversation_line_template` has missing template for line (ID: {}) in Conversation {}, skipped", currentConversationLine->ID, conversationTemplate.Id);
250
251 if (!currentConversationLine->NextConversationLineID)
252 break;
253
254 currentConversationLine = sConversationLineStore.AssertEntry(currentConversationLine->NextConversationLineID);
255 }
256
257 _conversationTemplateStore[conversationTemplate.Id] = std::move(conversationTemplate);
258 }
259 while (templates->NextRow());
260
261 TC_LOG_INFO("server.loading", ">> Loaded {} Conversation templates in {} ms", _conversationTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
262 }
263 else
264 {
265 TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation templates. DB table `conversation_template` is empty.");
266 }
267}
268
270{
271 return Trinity::Containers::MapGetValuePtr(_conversationTemplateStore, conversationId);
272}
273
275{
276 return Trinity::Containers::MapGetValuePtr(_conversationLineTemplateStore, conversationLineId);
277}
278
280{
281 static ConversationDataStore instance;
282 return &instance;
283}
ConversationFlags
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.
Definition: DatabaseEnv.cpp:20
uint32_t uint32
Definition: Define.h:142
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
#define sObjectMgr
Definition: ObjectMgr.h:1946
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:90
uint8 GetUInt8() const
Definition: Field.cpp:30
uint64 GetUInt64() const
Definition: Field.cpp:78
uint16 GetUInt16() const
Definition: Field.cpp:46
uint32 GetUInt32() const
Definition: Field.cpp:62
uint64 LowType
Definition: ObjectGuid.h:278
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
TC_PROTO_API ::google::protobuf::internal::ExtensionIdentifier< ::google::protobuf::FieldOptions, ::google::protobuf::internal::MessageTypeTraits< ::bgs::protocol::FieldRestriction >, 11, false > valid
std::variant< ConversationActorWorldObjectTemplate, ConversationActorNoObjectTemplate, ConversationActorActivePlayerTemplate, ConversationActorTalkingHeadTemplate > Data
EnumFlag< ConversationFlags > Flags
std::vector< ConversationLineTemplate const * > Lines
std::vector< ConversationActorTemplate > Actors