TrinityCore
Loading...
Searching...
No Matches
WorldStateMgr.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 "WorldStateMgr.h"
19#include "DB2Stores.h"
20#include "DatabaseEnv.h"
21#include "Log.h"
22#include "Map.h"
23#include "MapUtils.h"
24#include "ObjectMgr.h"
25#include "ScriptMgr.h"
26#include "StringConvert.h"
27#include "Util.h"
28#include "World.h"
29#include "WorldStatePackets.h"
30
31namespace
32{
33constexpr int32 WORLDSTATE_ANY_MAP = -1;
34struct Data
35{
36 std::unordered_map<int32, WorldStateTemplate> WorldStateTemplates;
37 std::mutex RealmWorldStateValuesMutex;
38 WorldStateValueContainer RealmWorldStateValues;
39 std::unordered_set<WorldStateValueContainer::value_type const*> ChangedRealmWorldStates;
40 std::vector<WorldPackets::WorldState::InitWorldStates::WorldStateInfo> RealmWorldStatePacketCache;
41 std::unordered_map<int32, WorldStateValueContainer> WorldStatesByMap;
42};
43
44Data& GetData()
45{
46 static Data data;
47 return data;
48}
49}
50
52{
53 uint32 oldMSTime = getMSTime();
54
55 // 0 1 2 3 4
56 QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapIDs, AreaIDs, ScriptName FROM world_state");
57 if (!result)
58 return;
59
60 Data& data = GetData();
61
62 do
63 {
64 Field* fields = result->Fetch();
65
66 int32 id = fields[0].GetInt32();
67 WorldStateTemplate& worldState = data.WorldStateTemplates[id];
68 worldState.Id = id;
69 worldState.DefaultValue = fields[1].GetInt32();
70
71 std::string_view mapIds = fields[2].GetStringView();
72 for (std::string_view mapIdToken : Trinity::Tokenize(mapIds, ',', false))
73 {
74 Optional<int32> mapId = Trinity::StringTo<int32>(mapIdToken);
75 if (!mapId)
76 {
77 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with non-integer MapID ({}), map ignored",
78 id, mapIdToken);
79 continue;
80 }
81
82 if (!sMapStore.LookupEntry(*mapId) && mapId != WORLDSTATE_ANY_MAP)
83 {
84 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with invalid MapID ({}), map ignored",
85 id, *mapId);
86 continue;
87 }
88
89 worldState.MapIds.insert(*mapId);
90 }
91
92 if (!mapIds.empty() && worldState.MapIds.empty())
93 {
94 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with nonempty MapIDs ({}) but no valid map id was found, ignored",
95 id, mapIds);
96 continue;
97 }
98
99 std::string_view areaIds = fields[3].GetStringView();
100 if (!worldState.MapIds.empty())
101 {
102 for (std::string_view areaIdToken : Trinity::Tokenize(areaIds, ',', false))
103 {
104 Optional<uint32> areaId = Trinity::StringTo<uint32>(areaIdToken);
105 if (!areaId)
106 {
107 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with non-integer AreaID ({}), area ignored",
108 id, areaIdToken);
109 continue;
110 }
111
112 AreaTableEntry const* areaTableEntry = sAreaTableStore.LookupEntry(*areaId);
113 if (!areaTableEntry)
114 {
115 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with invalid AreaID ({}), area ignored",
116 id, *areaId);
117 continue;
118 }
119
120 if (!worldState.MapIds.contains(areaTableEntry->ContinentID))
121 {
122 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with AreaID ({}) not on any of required maps, area ignored",
123 id, *areaId);
124 continue;
125 }
126
127 worldState.AreaIds.insert(*areaId);
128 }
129
130 if (!areaIds.empty() && worldState.AreaIds.empty())
131 {
132 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with nonempty AreaIDs ({}) but no valid area id was found, ignored",
133 id, areaIds);
134 continue;
135 }
136 }
137 else if (!areaIds.empty())
138 {
139 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with nonempty AreaIDs ({}) but is a realm wide world state, area requirement ignored",
140 id, areaIds);
141 }
142
143 worldState.ScriptId = sObjectMgr->GetScriptId(fields[4].GetStringView());
144
145 if (!worldState.MapIds.empty())
146 {
147 for (int32 mapId : worldState.MapIds)
148 data.WorldStatesByMap[mapId][id] = worldState.DefaultValue;
149 }
150 else
151 data.RealmWorldStateValues[id] = worldState.DefaultValue;
152
153 } while (result->NextRow());
154
155 TC_LOG_INFO("server.loading", ">> Loaded {} world state templates {} ms", data.WorldStateTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
156
157 oldMSTime = getMSTime();
158
159 result = CharacterDatabase.Query("SELECT Id, Value FROM world_state_value");
160 uint32 savedValueCount = 0;
161 if (result)
162 {
163 do
164 {
165 Field* fields = result->Fetch();
166 int32 worldStateId = fields[0].GetInt32();
167 WorldStateTemplate* worldState = Trinity::Containers::MapGetValuePtr(data.WorldStateTemplates, worldStateId);
168 if (!worldState)
169 {
170 TC_LOG_ERROR("sql.sql", "Table `world_state_value` contains a value for unknown world state {}, ignored", worldStateId);
171 continue;
172 }
173
174 int32 value = fields[1].GetInt32();
175
176 if (!worldState->MapIds.empty())
177 {
178 for (int32 mapId : worldState->MapIds)
179 data.WorldStatesByMap[mapId][worldStateId] = value;
180 }
181 else
182 data.RealmWorldStateValues[worldStateId] = value;
183
184 ++savedValueCount;
185 }
186 while (result->NextRow());
187 }
188
189 TC_LOG_INFO("server.loading", ">> Loaded {} saved world state values {} ms", savedValueCount, GetMSTimeDiffToNow(oldMSTime));
190}
191
193{
194 Data& data = GetData();
195 if (!data.ChangedRealmWorldStates.empty())
196 {
197 data.RealmWorldStatePacketCache.clear();
198 for (auto const& [worldStateId, value] : data.RealmWorldStateValues)
199 data.RealmWorldStatePacketCache.emplace_back(worldStateId, value);
200
201 // Broadcast update to all players on the server
202 for (WorldStateValueContainer::value_type const* changedRealmWorldState : data.ChangedRealmWorldStates)
203 {
205 updateWorldState.VariableID = changedRealmWorldState->first;
206 updateWorldState.Value = changedRealmWorldState->second;
207 updateWorldState.Hidden = false;
208 sWorld->SendGlobalMessage(updateWorldState.Write());
209 }
210
211 data.ChangedRealmWorldStates.clear();
212 }
213}
214
216{
217 return Trinity::Containers::MapGetValuePtr(GetData().WorldStateTemplates, worldStateId);
218}
219
220int32 WorldStateMgr::GetValue(int32 worldStateId, Map const* map)
221{
222 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
223 if (!worldStateTemplate || worldStateTemplate->MapIds.empty())
224 {
225 Data& data = GetData();
226 std::scoped_lock lock(data.RealmWorldStateValuesMutex);
227 if (int32 const* value = Trinity::Containers::MapGetValuePtr(data.RealmWorldStateValues, worldStateId))
228 return *value;
229
230 return 0;
231 }
232
233 if (!map || (!worldStateTemplate->MapIds.contains(map->GetId()) && !worldStateTemplate->MapIds.contains(WORLDSTATE_ANY_MAP)))
234 return 0;
235
236 return map->GetWorldStateValue(worldStateId);
237}
238
239void WorldStateMgr::SetValue(int32 worldStateId, int32 value, bool hidden, Map* map)
240{
241 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
242 if (!worldStateTemplate || worldStateTemplate->MapIds.empty())
243 {
244 int32 oldValue;
245 {
246 Data& data = GetData();
247 std::scoped_lock lock(data.RealmWorldStateValuesMutex);
248 auto [itr, inserted] = data.RealmWorldStateValues.try_emplace(worldStateId, 0);
249 oldValue = itr->second;
250 if (oldValue == value && !inserted)
251 return;
252
253 itr->second = value;
254 data.ChangedRealmWorldStates.insert(&*itr);
255 }
256
257 if (worldStateTemplate)
258 sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, nullptr);
259
260 return;
261 }
262
263 if (!map || (!worldStateTemplate->MapIds.contains(map->GetId()) && !worldStateTemplate->MapIds.contains(WORLDSTATE_ANY_MAP)))
264 return;
265
266 map->SetWorldStateValue(worldStateId, value, hidden);
267}
268
269void WorldStateMgr::SaveValueInDb(int32 worldStateId, int32 value)
270{
271 if (!GetWorldStateTemplate(worldStateId))
272 return;
273
275 stmt->setInt32(0, worldStateId);
276 stmt->setInt32(1, value);
277 CharacterDatabase.Execute(stmt);
278}
279
280void WorldStateMgr::SetValueAndSaveInDb(int32 worldStateId, int32 value, bool hidden, Map* map)
281{
282 SetValue(worldStateId, value, hidden, map);
283 SaveValueInDb(worldStateId, value);
284}
285
287{
288 WorldStateValueContainer initialValues;
289 Data& data = GetData();
290 if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(data.WorldStatesByMap, map->GetId()))
291 initialValues.insert(valuesTemplate->begin(), valuesTemplate->end());
292
293 if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(data.WorldStatesByMap, WORLDSTATE_ANY_MAP))
294 initialValues.insert(valuesTemplate->begin(), valuesTemplate->end());
295
296 return initialValues;
297}
298
300{
301 Data& data = GetData();
302 initWorldStates.Worldstates.insert(initWorldStates.Worldstates.end(), data.RealmWorldStatePacketCache.begin(), data.RealmWorldStatePacketCache.end());
303
304 for (auto const& [worldStateId, value] : map->GetWorldStateValues())
305 {
306 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
307 if (worldStateTemplate && !worldStateTemplate->AreaIds.empty())
308 {
309 bool isInAllowedArea = std::ranges::any_of(worldStateTemplate->AreaIds,
310 [=](uint32 requiredAreaId) { return DB2Manager::IsInArea(playerAreaId, requiredAreaId); });
311 if (!isInAllowedArea)
312 continue;
313 }
314
315 initWorldStates.Worldstates.emplace_back(worldStateId, value);
316 }
317}
@ CHAR_REP_WORLD_STATE
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
int32_t int32
Definition Define.h:150
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
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define sScriptMgr
Definition ScriptMgr.h:1449
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
std::unordered_map< int32, int32 > WorldStateValueContainer
Class used to access individual fields of database query result.
Definition Field.h:94
std::string_view GetStringView() const noexcept
Definition Field.cpp:118
int32 GetInt32() const noexcept
Definition Field.cpp:64
Definition Map.h:225
WorldStateValueContainer const & GetWorldStateValues() const
Definition Map.h:845
void SetWorldStateValue(int32 worldStateId, int32 value, bool hidden)
Definition Map.cpp:435
int32 GetWorldStateValue(int32 worldStateId) const
Definition Map.cpp:427
uint32 GetId() const
Definition Map.cpp:3257
void setInt32(uint8 index, int32 value)
std::vector< WorldStateInfo > Worldstates
#define sWorld
Definition World.h:916
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition Util.cpp:57
TC_GAME_API void SetValue(int32 worldStateId, int32 value, bool hidden, Map *map)
TC_GAME_API void SetValueAndSaveInDb(int32 worldStateId, int32 value, bool hidden, Map *map)
TC_GAME_API void SaveValueInDb(int32 worldStateId, int32 value)
void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates &initWorldStates, Map const *map, uint32 playerAreaId)
TC_GAME_API WorldStateTemplate const * GetWorldStateTemplate(int32 worldStateId)
TC_GAME_API int32 GetValue(int32 worldStateId, Map const *map)
WorldStateValueContainer GetInitialWorldStatesForMap(Map const *map)
std::unordered_set< uint32 > AreaIds
std::unordered_set< uint32 > MapIds