TrinityCore
Loading...
Searching...
No Matches
MapManager.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 "MapManager.h"
19#include "BattlefieldMgr.h"
20#include "Battleground.h"
21#include "BattlegroundScript.h"
22#include "CharacterCache.h"
23#include "Containers.h"
24#include "DatabaseEnv.h"
25#include "DB2Stores.h"
26#include "GarrisonMap.h"
27#include "Group.h"
28#include "InstanceLockMgr.h"
29#include "Log.h"
30#include "Map.h"
31#include "OutdoorPvPMgr.h"
32#include "Player.h"
33#include "ScenarioMgr.h"
34#include "ScriptMgr.h"
35#include "World.h"
36#include "WorldStateMgr.h"
37
38#include <boost/dynamic_bitset.hpp>
39#include <numeric>
40
42 : _freeInstanceIds(std::make_unique<InstanceIds>()), _nextInstanceId(0), _scheduledScripts(0)
43{
46}
47
48MapManager::~MapManager() = default;
49
51{
53
54 int num_threads(sWorld->getIntConfig(CONFIG_NUMTHREADS));
55 // Start mtmaps if needed.
56 if (num_threads > 0)
57 m_updater.activate(num_threads);
58}
59
61{
62 for (auto iter = i_maps.begin(); iter != i_maps.end(); ++iter)
63 iter->second->InitVisibilityDistance();
64}
65
71
72Map* MapManager::FindMap_i(uint32 mapId, uint32 instanceId) const
73{
74 auto itr = i_maps.find({ mapId, instanceId });
75 return itr != i_maps.end() ? itr->second.get() : nullptr;
76}
77
79{
80 Map* map = new Map(mapId, i_gridCleanUpDelay, instanceId, DIFFICULTY_NONE);
81 map->LoadRespawnTimes();
82 map->LoadCorpseData();
84
85 if (sWorld->getBoolConfig(CONFIG_BASEMAP_LOAD_GRIDS))
86 map->LoadAllCells();
87
88 return map;
89}
90
91InstanceMap* MapManager::CreateInstance(uint32 mapId, uint32 instanceId, InstanceLock* instanceLock, Difficulty difficulty, TeamId team, Group* group,
92 Optional<uint32> lfgDungeonsId)
93{
94 // make sure we have a valid map id
95 MapEntry const* entry = sMapStore.LookupEntry(mapId);
96 if (!entry)
97 {
98 TC_LOG_ERROR("maps", "CreateInstance: no entry for map {}", mapId);
99 ABORT();
100 }
101
102 // some instances only have one difficulty
103 sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty);
104
105 TC_LOG_DEBUG("maps", "MapInstanced::CreateInstance: {}map instance {} for {} created with difficulty {}",
106 instanceLock && instanceLock->IsNew() ? "" : "new ", instanceId, mapId, DB2Manager::GetDifficultyName(difficulty));
107
108 InstanceMap* map = new InstanceMap(mapId, i_gridCleanUpDelay, instanceId, difficulty, team, instanceLock, lfgDungeonsId);
109 ASSERT(map->IsDungeon());
110
111 map->LoadRespawnTimes();
112 map->LoadCorpseData();
113 if (group)
114 map->TrySetOwningGroup(group);
115
116 map->CreateInstanceData();
117 map->SetInstanceScenario(sScenarioMgr->CreateInstanceScenarioForTeam(map, team));
118 map->InitSpawnGroupState();
119
120 if (sWorld->getBoolConfig(CONFIG_INSTANCEMAP_LOAD_GRIDS))
121 map->LoadAllCells();
122
123 return map;
124}
125
127{
128 TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg {} for {} created.", instanceId, mapId);
129
132 map->SetBG(bg);
133 bg->SetBgMap(map);
134 map->InitScriptData();
135 map->InitSpawnGroupState();
137
138 if (sWorld->getBoolConfig(CONFIG_BATTLEGROUNDMAP_LOAD_GRIDS))
139 map->LoadAllCells();
140
141 return map;
142}
143
145{
146 GarrisonMap* map = new GarrisonMap(mapId, i_gridCleanUpDelay, instanceId, owner->GetGUID());
147 ASSERT(map->IsGarrison());
148 map->InitSpawnGroupState();
149 return map;
150}
151
152/*
153- return the right instance for the object, based on its InstanceId
154- create the instance if it's not created already
155- the player is not actually added to the instance (only in InstanceMap::Add)
156*/
157Map* MapManager::CreateMap(uint32 mapId, Player* player, Optional<uint32> lfgDungeonsId /*= {}*/)
158{
159 if (!player)
160 return nullptr;
161
162 MapEntry const* entry = sMapStore.LookupEntry(mapId);
163 if (!entry)
164 return nullptr;
165
166 std::scoped_lock lock(_mapsLock);
167
168 Map* map = nullptr;
169 uint32 newInstanceId = 0; // instanceId of the resulting map
170
171 if (entry->IsBattlegroundOrArena())
172 {
173 // instantiate or find existing bg map for player
174 // the instance id is set in battlegroundid
175 newInstanceId = player->GetBattlegroundId();
176 if (!newInstanceId)
177 return nullptr;
178
179 map = FindMap_i(mapId, newInstanceId);
180 if (!map)
181 {
182 if (Battleground* bg = player->GetBattleground())
183 map = CreateBattleground(mapId, newInstanceId, bg);
184 else
185 {
186 player->TeleportToBGEntryPoint();
187 return nullptr;
188 }
189 }
190 }
191 else if (entry->IsDungeon())
192 {
193 Group* group = player->GetGroup();
194 Difficulty difficulty = group ? group->GetDifficultyID(entry) : player->GetDifficultyID(entry);
195 MapDb2Entries entries{ entry, sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty) };
196 ObjectGuid instanceOwnerGuid = group ? group->GetRecentInstanceOwner(mapId) : player->GetGUID();
197 InstanceLock* instanceLock = sInstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, entries);
198 if (instanceLock)
199 {
200 newInstanceId = instanceLock->GetInstanceId();
201
202 // Reset difficulty to the one used in instance lock
203 if (!entries.Map->IsFlexLocking())
204 difficulty = instanceLock->GetDifficultyId();
205 }
206 else
207 {
208 // Try finding instance id for normal dungeon
209 if (!entries.MapDifficulty->HasResetSchedule())
210 newInstanceId = group ? group->GetRecentInstanceId(mapId) : player->GetRecentInstanceId(mapId);
211
212 // If not found or instance is not a normal dungeon, generate new one
213 if (!newInstanceId)
214 newInstanceId = GenerateInstanceId();
215
216 instanceLock = sInstanceLockMgr.CreateInstanceLockForNewInstance(instanceOwnerGuid, entries, newInstanceId);
217 }
218
219 // it is possible that the save exists but the map doesn't
220 map = FindMap_i(mapId, newInstanceId);
221
222 // is is also possible that instance id is already in use by another group for boss-based locks
223 if (!entries.IsInstanceIdBound() && instanceLock && map && map->ToInstanceMap()->GetInstanceLock() != instanceLock)
224 {
225 newInstanceId = GenerateInstanceId();
226 instanceLock->SetInstanceId(newInstanceId);
227 map = nullptr;
228 }
229
230 if (!map)
231 {
232 map = CreateInstance(mapId, newInstanceId, instanceLock, difficulty, GetTeamIdForTeam(sCharacterCache->GetCharacterTeamByGuid(instanceOwnerGuid)), group,
233 lfgDungeonsId);
234 if (group)
235 group->SetRecentInstance(mapId, instanceOwnerGuid, newInstanceId);
236 else
237 player->SetRecentInstance(mapId, newInstanceId);
238 }
239 }
240 else if (entry->IsGarrison())
241 {
242 newInstanceId = player->GetGUID().GetCounter();
243 map = FindMap_i(mapId, newInstanceId);
244 if (!map)
245 map = CreateGarrison(mapId, newInstanceId, player);
246 }
247 else
248 {
249 newInstanceId = 0;
250 if (entry->IsSplitByFaction())
251 newInstanceId = player->GetTeamId();
252
253 map = FindMap_i(mapId, newInstanceId);
254 if (!map)
255 map = CreateWorldMap(mapId, newInstanceId);
256 }
257
258 if (map)
259 {
261 if (ptr.get() != map)
262 {
263 ptr.reset(map);
264 map->SetWeakPtr(ptr);
265
266 sScriptMgr->OnCreateMap(map);
267 sOutdoorPvPMgr->CreateOutdoorPvPForMap(map);
268 sBattlefieldMgr->CreateBattlefieldsForMap(map);
269 }
270 }
271
272 return map;
273}
274
275Map* MapManager::FindMap(uint32 mapId, uint32 instanceId) const
276{
277 std::shared_lock<std::shared_mutex> lock(_mapsLock);
278 return FindMap_i(mapId, instanceId);
279}
280
282{
283 MapEntry const* entry = sMapStore.LookupEntry(mapId);
284 if (!entry)
285 return 0;
286
287 if (entry->IsBattlegroundOrArena())
288 return player->GetBattlegroundId();
289 else if (entry->IsDungeon())
290 {
291 Group const* group = player->GetGroup();
292 Difficulty difficulty = group ? group->GetDifficultyID(entry) : player->GetDifficultyID(entry);
293 MapDb2Entries entries{ entry, sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty) };
294 ObjectGuid instanceOwnerGuid = group ? group->GetRecentInstanceOwner(mapId) : player->GetGUID();
295 InstanceLock* instanceLock = sInstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, entries);
296 uint32 newInstanceId = 0;
297 if (instanceLock)
298 newInstanceId = instanceLock->GetInstanceId();
299 else if (!entries.MapDifficulty->HasResetSchedule()) // Try finding instance id for normal dungeon
300 newInstanceId = group ? group->GetRecentInstanceId(mapId) : player->GetRecentInstanceId(mapId);
301
302 if (!newInstanceId)
303 return 0;
304
305 Map* map = FindMap(mapId, newInstanceId);
306
307 // is is possible that instance id is already in use by another group for boss-based locks
308 if (!entries.IsInstanceIdBound() && instanceLock && map && map->ToInstanceMap()->GetInstanceLock() != instanceLock)
309 return 0;
310
311 return newInstanceId;
312 }
313 else if (entry->IsGarrison())
314 return uint32(player->GetGUID().GetCounter());
315 else
316 {
317 if (entry->IsSplitByFaction())
318 return player->GetTeamId();
319
320 return 0;
321 }
322}
323
325{
326 i_timer.Update(diff);
327 if (!i_timer.Passed())
328 return;
329
330 MapMapType::iterator iter = i_maps.begin();
331 while (iter != i_maps.end())
332 {
333 if (iter->second->CanUnload(uint32(i_timer.GetCurrent())))
334 {
335 if (DestroyMap(iter->second.get()))
336 iter = i_maps.erase(iter);
337 else
338 ++iter;
339
340 continue;
341 }
342
343 if (m_updater.activated())
345 else
346 iter->second->Update(uint32(i_timer.GetCurrent()));
347
348 ++iter;
349 }
350 if (m_updater.activated())
351 m_updater.wait();
352
353 for (iter = i_maps.begin(); iter != i_maps.end(); ++iter)
354 iter->second->DelayedUpdate(uint32(i_timer.GetCurrent()));
355
357}
358
360{
361 map->RemoveAllPlayers();
362 if (map->HavePlayers())
363 return false;
364
365 sOutdoorPvPMgr->DestroyOutdoorPvPForMap(map);
366 sBattlefieldMgr->DestroyBattlefieldsForMap(map);
367 sScriptMgr->OnDestroyMap(map);
368
369 map->UnloadAll();
370
371 // Free up the instance id and allow it to be reused for normal dungeons, bgs and arenas
372 if (map->IsBattlegroundOrArena() || (map->IsDungeon() && !map->GetMapDifficulty()->HasResetSchedule()))
373 sMapMgr->FreeInstanceId(map->GetInstanceId());
374
375 // erase map
376 return true;
377}
378
380{
381 return sMapStore.LookupEntry(mapId) != nullptr;
382}
383
385{
386 // first unload maps
387 for (auto iter = i_maps.begin(); iter != i_maps.end(); ++iter)
388 {
389 iter->second->UnloadAll();
390
391 sOutdoorPvPMgr->DestroyOutdoorPvPForMap(iter->second.get());
392 sBattlefieldMgr->DestroyBattlefieldsForMap(iter->second.get());
393 sScriptMgr->OnDestroyMap(iter->second.get());
394 }
395
396 // then delete them
397 i_maps.clear();
398
399 if (m_updater.activated())
401
403}
404
406{
407 std::shared_lock<std::shared_mutex> lock(_mapsLock);
408 return std::count_if(i_maps.begin(), i_maps.end(), [](MapMapType::value_type const& value) { return value.second->IsDungeon(); });
409}
410
412{
413 std::shared_lock<std::shared_mutex> lock(_mapsLock);
414 return std::accumulate(i_maps.begin(), i_maps.end(), 0u, [](uint32 total, MapMapType::value_type const& value) { return total + (value.second->IsDungeon() ? value.second->GetPlayers().size() : 0); });
415}
416
418{
419 _nextInstanceId = 1;
420
421 uint64 maxExistingInstanceId = 0;
422 if (QueryResult result = CharacterDatabase.Query("SELECT IFNULL(MAX(instanceId), 0) FROM instance"))
423 maxExistingInstanceId = std::max(maxExistingInstanceId, (*result)[0].GetUInt64());
424
425 if (QueryResult result = CharacterDatabase.Query("SELECT IFNULL(MAX(instanceId), 0) FROM character_instance_lock"))
426 maxExistingInstanceId = std::max(maxExistingInstanceId, (*result)[0].GetUInt64());
427
428 _freeInstanceIds->resize(maxExistingInstanceId + 2, true); // make space for one extra to be able to access [_nextInstanceId] index in case all slots are taken
429
430 // never allow 0 id
431 _freeInstanceIds->set(0, false);
432}
433
435{
436 // Allocation and sizing was done in InitInstanceIds()
437 _freeInstanceIds->set(instanceId, false);
438
439 // Instances are pulled in ascending order from db and nextInstanceId is initialized with 1,
440 // so if the instance id is used, increment until we find the first unused one for a potential new instance
441 if (_nextInstanceId == instanceId)
443}
444
446{
447 if (_nextInstanceId == 0xFFFFFFFF)
448 {
449 TC_LOG_ERROR("maps", "Instance ID overflow!! Can't continue, shutting down server. ");
451 return _nextInstanceId;
452 }
453
454 uint32 newInstanceId = _nextInstanceId;
455 ASSERT(newInstanceId < _freeInstanceIds->size());
456 _freeInstanceIds->set(newInstanceId, false);
457
458 // Find the lowest available id starting from the current NextInstanceId (which should be the lowest according to the logic in FreeInstanceId())
459 size_t nextFreedId = _freeInstanceIds->find_next(_nextInstanceId++);
460 if (nextFreedId == InstanceIds::npos)
461 {
463 _freeInstanceIds->push_back(true);
464 }
465 else
466 _nextInstanceId = uint32(nextFreedId);
467
468 return newInstanceId;
469}
470
472{
473 // If freed instance id is lower than the next id available for new instances, use the freed one instead
474 _nextInstanceId = std::min(instanceId, _nextInstanceId);
475 _freeInstanceIds->set(instanceId, true);
476}
477
478// hack to allow conditions to access what faction owns the map (these worldstates should not be set on these maps)
480{
481public:
482 SplitByFactionMapScript(char const* name, uint32 mapId) : WorldMapScript(name, mapId)
483 {
484 }
485
491};
492
494{
495 for (MapEntry const* mapEntry : sMapStore)
496 if (mapEntry->IsWorldMap() && mapEntry->IsSplitByFaction())
497 new SplitByFactionMapScript(Trinity::StringFormat("world_map_set_faction_worldstates_{}", mapEntry->ID).c_str(), mapEntry->ID);
498}
#define sBattlefieldMgr
#define sCharacterCache
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
Difficulty
Definition DBCEnums.h:932
@ DIFFICULTY_NONE
Definition DBCEnums.h:933
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint64_t uint64
Definition Define.h:153
uint32_t uint32
Definition Define.h:154
#define ABORT
Definition Errors.h:87
#define ASSERT
Definition Errors.h:80
#define sInstanceLockMgr
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define sMapMgr
Definition MapManager.h:186
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define sOutdoorPvPMgr
#define sScenarioMgr
#define sScriptMgr
Definition ScriptMgr.h:1449
constexpr TeamId GetTeamIdForTeam(Team team)
@ TEAM_ALLIANCE
@ TEAM_HORDE
@ WS_TEAM_IN_INSTANCE_ALLIANCE
@ WS_TEAM_IN_INSTANCE_HORDE
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
void InitScriptData()
Definition Map.cpp:3445
void SetBG(Battleground *bg)
Definition Map.h:938
BattlegroundScript * GetBattlegroundScript()
Definition Map.h:942
void SetBgMap(BattlegroundMap *map)
static std::string_view GetDifficultyName(Difficulty difficulty)
Definition Group.h:205
uint32 GetRecentInstanceId(uint32 mapId) const
Definition Group.h:390
void SetRecentInstance(uint32 mapId, ObjectGuid instanceOwner, uint32 instanceId)
Definition Group.h:396
ObjectGuid GetRecentInstanceOwner(uint32 mapId) const
Definition Group.h:384
Difficulty GetDifficultyID(MapEntry const *mapEntry) const
Definition Group.cpp:1348
void SetInstanceId(uint32 instanceId)
Difficulty GetDifficultyId() const
bool IsNew() const
uint32 GetInstanceId() const
InstanceLock const * GetInstanceLock() const
Definition Map.h:895
void RegisterInstanceId(uint32 instanceId)
std::unique_ptr< InstanceIds > _freeInstanceIds
Definition MapManager.h:155
std::shared_mutex _mapsLock
Definition MapManager.h:150
bool DestroyMap(Map *map)
void Update(uint32 diff)
uint32 FindInstanceIdForPlayer(uint32 mapId, Player const *player) const
MapUpdater m_updater
Definition MapManager.h:157
boost::dynamic_bitset< size_t > InstanceIds
Definition MapManager.h:138
static MapManager * instance()
InstanceMap * CreateInstance(uint32 mapId, uint32 instanceId, InstanceLock *instanceLock, Difficulty difficulty, TeamId team, Group *group, Optional< uint32 > lfgDungeonsId)
uint32 i_gridCleanUpDelay
Definition MapManager.h:151
Map * FindMap_i(uint32 mapId, uint32 instanceId) const
GarrisonMap * CreateGarrison(uint32 mapId, uint32 instanceId, Player *owner)
Map * FindMap(uint32 mapId, uint32 instanceId) const
void InitializeVisibilityDistanceInfo()
void UnloadAll()
uint32 GetNumPlayersInInstances() const
Map * CreateMap(uint32 mapId, Player *player, Optional< uint32 > lfgDungeonsId={})
static bool IsValidMAP(uint32 mapId)
uint32 _nextInstanceId
Definition MapManager.h:156
MapMapType i_maps
Definition MapManager.h:152
void InitInstanceIds()
Map * CreateWorldMap(uint32 mapId, uint32 instanceId)
void AddSC_BuiltInScripts()
BattlegroundMap * CreateBattleground(uint32 mapId, uint32 instanceId, Battleground *bg)
uint32 GenerateInstanceId()
IntervalTimer i_timer
Definition MapManager.h:153
uint32 GetNumInstances() const
void FreeInstanceId(uint32 instanceId)
void Initialize()
void schedule_update(Map &map, uint32 diff)
void activate(size_t num_threads)
void wait()
bool activated() const
void deactivate()
Definition Map.h:225
bool IsDungeon() const
Definition Map.cpp:3267
bool IsBattlegroundOrArena() const
Definition Map.cpp:3369
void SetWeakPtr(Trinity::unique_weak_ptr< Map > weakRef)
Definition Map.h:353
MapDifficultyEntry const * GetMapDifficulty() const
Definition Map.cpp:3252
void UnloadAll()
Definition Map.cpp:1652
bool HavePlayers() const
Definition Map.h:393
void LoadRespawnTimes()
Definition Map.cpp:3661
virtual void RemoveAllPlayers()
Definition Map.cpp:1635
static void DeleteStateMachine()
Definition Map.cpp:129
void LoadCorpseData()
Definition Map.cpp:3721
uint32 GetId() const
Definition Map.cpp:3257
InstanceMap * ToInstanceMap()
Definition Map.h:490
void InitSpawnGroupState()
Definition Map.cpp:2490
uint32 GetInstanceId() const
Definition Map.h:350
static void InitStateMachine()
Definition Map.cpp:121
void LoadAllCells()
Definition Map.cpp:114
bool IsGarrison() const
Definition Map.cpp:3379
LowType GetCounter() const
Definition ObjectGuid.h:336
void SetRecentInstance(uint32 mapId, uint32 instanceId)
Definition Player.h:2769
uint32 GetRecentInstanceId(uint32 mapId) const
Definition Player.h:2763
TeamId GetTeamId() const
Definition Player.h:2424
bool TeleportToBGEntryPoint()
Definition Player.cpp:1478
Battleground * GetBattleground() const
Definition Player.cpp:25719
uint32 GetBattlegroundId() const
Definition Player.h:2586
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2796
Difficulty GetDifficultyID(MapEntry const *mapEntry) const
Definition Player.cpp:30627
iterator end()
Definition RefManager.h:36
SplitByFactionMapScript(char const *name, uint32 mapId)
void OnCreate(Map *map) override
Specialized variant of std::shared_ptr that enforces unique ownership and/or std::unique_ptr with std...
static void StopNow(uint8 exitcode)
Definition World.h:667
#define sWorld
Definition World.h:916
@ CONFIG_INTERVAL_GRIDCLEAN
Definition World.h:239
@ CONFIG_INTERVAL_MAPUPDATE
Definition World.h:240
@ CONFIG_NUMTHREADS
Definition World.h:354
@ CONFIG_INSTANCEMAP_LOAD_GRIDS
Definition World.h:181
@ CONFIG_BASEMAP_LOAD_GRIDS
Definition World.h:180
@ CONFIG_BATTLEGROUNDMAP_LOAD_GRIDS
Definition World.h:198
@ ERROR_EXIT_CODE
Definition World.h:76
std::string StringFormat(FormatString< Args... > fmt, Args &&... args) noexcept
Default TC string format function.
TC_GAME_API void SetValue(int32 worldStateId, int32 value, bool hidden, Map *map)
STL namespace.
void SetInterval(time_t interval)
Definition Timer.h:94
time_t GetCurrent() const
Definition Timer.h:104
bool Passed()
Definition Timer.h:78
void Update(time_t diff)
Definition Timer.h:71
void SetCurrent(time_t current)
Definition Timer.h:89
bool HasResetSchedule() const
bool IsSplitByFaction() const
bool IsBattlegroundOrArena() const
bool IsGarrison() const
bool IsDungeon() const