TrinityCore
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 "Containers.h"
20#include "DatabaseEnv.h"
21#include "DB2Stores.h"
22#include "Log.h"
23#include "Map.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;
34std::unordered_map<int32, WorldStateTemplate> _worldStateTemplates;
35WorldStateValueContainer _realmWorldStateValues;
36std::unordered_map<int32, WorldStateValueContainer> _worldStatesByMap;
37}
38
40{
41 uint32 oldMSTime = getMSTime();
42
43 // 0 1 2 3 4
44 QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapIDs, AreaIDs, ScriptName FROM world_state");
45 if (!result)
46 return;
47
48 do
49 {
50 Field* fields = result->Fetch();
51
52 int32 id = fields[0].GetInt32();
53 WorldStateTemplate& worldState = _worldStateTemplates[id];
54 worldState.Id = id;
55 worldState.DefaultValue = fields[1].GetInt32();
56
57 std::string_view mapIds = fields[2].GetStringView();
58 for (std::string_view mapIdToken : Trinity::Tokenize(mapIds, ',', false))
59 {
60 Optional<int32> mapId = Trinity::StringTo<int32>(mapIdToken);
61 if (!mapId)
62 {
63 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with non-integer MapID ({}), map ignored",
64 id, mapIdToken);
65 continue;
66 }
67
68 if (!sMapStore.LookupEntry(*mapId) && mapId != WORLDSTATE_ANY_MAP)
69 {
70 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with invalid MapID ({}), map ignored",
71 id, *mapId);
72 continue;
73 }
74
75 worldState.MapIds.insert(*mapId);
76 }
77
78 if (!mapIds.empty() && worldState.MapIds.empty())
79 {
80 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with nonempty MapIDs ({}) but no valid map id was found, ignored",
81 id, mapIds);
82 continue;
83 }
84
85 std::string_view areaIds = fields[3].GetStringView();
86 if (!worldState.MapIds.empty())
87 {
88 for (std::string_view areaIdToken : Trinity::Tokenize(areaIds, ',', false))
89 {
90 Optional<uint32> areaId = Trinity::StringTo<uint32>(areaIdToken);
91 if (!areaId)
92 {
93 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with non-integer AreaID ({}), area ignored",
94 id, areaIdToken);
95 continue;
96 }
97
98 AreaTableEntry const* areaTableEntry = sAreaTableStore.LookupEntry(*areaId);
99 if (!areaTableEntry)
100 {
101 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with invalid AreaID ({}), area ignored",
102 id, *areaId);
103 continue;
104 }
105
106 if (worldState.MapIds.find(areaTableEntry->ContinentID) == worldState.MapIds.end())
107 {
108 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with AreaID ({}) not on any of required maps, area ignored",
109 id, *areaId);
110 continue;
111 }
112
113 worldState.AreaIds.insert(*areaId);
114 }
115
116 if (!areaIds.empty() && worldState.AreaIds.empty())
117 {
118 TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state {} with nonempty AreaIDs ({}) but no valid area id was found, ignored",
119 id, areaIds);
120 continue;
121 }
122 }
123 else if (!areaIds.empty())
124 {
125 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",
126 id, areaIds);
127 }
128
129 worldState.ScriptId = sObjectMgr->GetScriptId(fields[4].GetString());
130
131 if (!worldState.MapIds.empty())
132 {
133 for (int32 mapId : worldState.MapIds)
134 _worldStatesByMap[mapId][id] = worldState.DefaultValue;
135 }
136 else
137 _realmWorldStateValues[id] = worldState.DefaultValue;
138
139 } while (result->NextRow());
140
141 TC_LOG_INFO("server.loading", ">> Loaded {} world state templates {} ms", _worldStateTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
142
143 oldMSTime = getMSTime();
144
145 result = CharacterDatabase.Query("SELECT Id, Value FROM world_state_value");
146 uint32 savedValueCount = 0;
147 if (result)
148 {
149 do
150 {
151 Field* fields = result->Fetch();
152 int32 worldStateId = fields[0].GetInt32();
153 WorldStateTemplate* worldState = Trinity::Containers::MapGetValuePtr(_worldStateTemplates, worldStateId);
154 if (!worldState)
155 {
156 TC_LOG_ERROR("sql.sql", "Table `world_state_value` contains a value for unknown world state {}, ignored", worldStateId);
157 continue;
158 }
159
160 int32 value = fields[1].GetInt32();
161
162 if (!worldState->MapIds.empty())
163 {
164 for (int32 mapId : worldState->MapIds)
165 _worldStatesByMap[mapId][worldStateId] = value;
166 }
167 else
168 _realmWorldStateValues[worldStateId] = value;
169
170 ++savedValueCount;
171 }
172 while (result->NextRow());
173 }
174
175 TC_LOG_INFO("server.loading", ">> Loaded {} saved world state values {} ms", savedValueCount, GetMSTimeDiffToNow(oldMSTime));
176}
177
179{
180 return Trinity::Containers::MapGetValuePtr(_worldStateTemplates, worldStateId);
181}
182
183int32 WorldStateMgr::GetValue(int32 worldStateId, Map const* map) const
184{
185 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
186 if (!worldStateTemplate || worldStateTemplate->MapIds.empty())
187 {
188 if (int32 const* value = Trinity::Containers::MapGetValuePtr(_realmWorldStateValues, worldStateId))
189 return *value;
190
191 return 0;
192 }
193
194 if (!map || (!worldStateTemplate->MapIds.count(map->GetId()) && !worldStateTemplate->MapIds.count(WORLDSTATE_ANY_MAP)))
195 return 0;
196
197 return map->GetWorldStateValue(worldStateId);
198}
199
200void WorldStateMgr::SetValue(int32 worldStateId, int32 value, bool hidden, Map* map)
201{
202 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
203 if (!worldStateTemplate || worldStateTemplate->MapIds.empty())
204 {
205 auto [itr, inserted] = _realmWorldStateValues.try_emplace(worldStateId, 0);
206 int32 oldValue = itr->second;
207 if (oldValue == value && !inserted)
208 return;
209
210 itr->second = value;
211
212 if (worldStateTemplate)
213 sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, nullptr);
214
215 // Broadcast update to all players on the server
217 updateWorldState.VariableID = worldStateId;
218 updateWorldState.Value = value;
219 updateWorldState.Hidden = hidden;
220 sWorld->SendGlobalMessage(updateWorldState.Write());
221 return;
222 }
223
224 if (!map || (!worldStateTemplate->MapIds.count(map->GetId()) && !worldStateTemplate->MapIds.count(WORLDSTATE_ANY_MAP)))
225 return;
226
227 map->SetWorldStateValue(worldStateId, value, hidden);
228}
229
230void WorldStateMgr::SaveValueInDb(int32 worldStateId, int32 value)
231{
232 if (!GetWorldStateTemplate(worldStateId))
233 return;
234
236 stmt->setInt32(0, worldStateId);
237 stmt->setInt32(1, value);
238 CharacterDatabase.Execute(stmt);
239}
240
241void WorldStateMgr::SetValueAndSaveInDb(int32 worldStateId, int32 value, bool hidden, Map* map)
242{
243 SetValue(worldStateId, value, hidden, map);
244 SaveValueInDb(worldStateId, value);
245}
246
248{
249 WorldStateValueContainer initialValues;
250 if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(_worldStatesByMap, map->GetId()))
251 initialValues.insert(valuesTemplate->begin(), valuesTemplate->end());
252
253 if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(_worldStatesByMap, WORLDSTATE_ANY_MAP))
254 initialValues.insert(valuesTemplate->begin(), valuesTemplate->end());
255
256 return initialValues;
257}
258
260{
261 for (auto const& [worldStateId, value] : _realmWorldStateValues)
262 initWorldStates.Worldstates.emplace_back(worldStateId, value);
263
264 for (auto const& [worldStateId, value] : map->GetWorldStateValues())
265 {
266 WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
267 if (worldStateTemplate && !worldStateTemplate->AreaIds.empty())
268 {
269 bool isInAllowedArea = std::any_of(worldStateTemplate->AreaIds.begin(), worldStateTemplate->AreaIds.end(),
270 [=](uint32 requiredAreaId) { return DB2Manager::IsInArea(playerAreaId, requiredAreaId); });
271 if (!isInAllowedArea)
272 continue;
273 }
274
275 initWorldStates.Worldstates.emplace_back(worldStateId, value);
276 }
277}
278
280{
281 static WorldStateMgr instance;
282 return &instance;
283}
@ 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.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
int32_t int32
Definition: Define.h:138
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
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
#define sScriptMgr
Definition: ScriptMgr.h:1418
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:90
std::string_view GetStringView() const
Definition: Field.cpp:130
int32 GetInt32() const
Definition: Field.cpp:70
Definition: Map.h:189
WorldStateValueContainer const & GetWorldStateValues() const
Definition: Map.h:826
void SetWorldStateValue(int32 worldStateId, int32 value, bool hidden)
Definition: Map.cpp:468
int32 GetWorldStateValue(int32 worldStateId) const
Definition: Map.cpp:460
uint32 GetId() const
Definition: Map.cpp:3228
void setInt32(const uint8 index, const int32 value)
std::vector< WorldStateInfo > Worldstates
void SetValue(int32 worldStateId, int32 value, bool hidden, Map *map)
WorldStateTemplate const * GetWorldStateTemplate(int32 worldStateId) const
void SetValueAndSaveInDb(int32 worldStateId, int32 value, bool hidden, Map *map)
void SaveValueInDb(int32 worldStateId, int32 value)
void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates &initWorldStates, Map const *map, uint32 playerAreaId) const
static WorldStateMgr * instance()
int32 GetValue(int32 worldStateId, Map const *map) const
WorldStateValueContainer GetInitialWorldStatesForMap(Map const *map) const
#define sWorld
Definition: World.h:931
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
uint16 ContinentID
Definition: DB2Structure.h:130
std::unordered_set< uint32 > AreaIds
std::unordered_set< uint32 > MapIds