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->LoadAllGrids();
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 TC_LOG_DEBUG("maps", "MapInstanced::CreateInstance: {}map instance {} for {} created with difficulty {}",
103 instanceLock && instanceLock->IsNew() ? "" : "new ", instanceId, mapId, DB2Manager::GetDifficultyName(difficulty));
104
105 InstanceMap* map = new InstanceMap(mapId, i_gridCleanUpDelay, instanceId, difficulty, team, instanceLock, lfgDungeonsId);
106 ASSERT(map->IsDungeon());
107
108 map->LoadRespawnTimes();
109 map->LoadCorpseData();
110 if (group)
111 map->TrySetOwningGroup(group);
112
113 map->CreateInstanceData();
114 map->SetInstanceScenario(sScenarioMgr->CreateInstanceScenarioForTeam(map, team));
115 map->InitSpawnGroupState();
116
117 if (sWorld->getBoolConfig(CONFIG_INSTANCEMAP_LOAD_GRIDS))
118 map->LoadAllGrids();
119
120 return map;
121}
122
124{
125 TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg {} for {} created.", instanceId, mapId);
126
129 map->SetBG(bg);
130 bg->SetBgMap(map);
131 map->InitScriptData();
132 map->InitSpawnGroupState();
134
135 if (sWorld->getBoolConfig(CONFIG_BATTLEGROUNDMAP_LOAD_GRIDS))
136 map->LoadAllGrids();
137
138 return map;
139}
140
142{
143 GarrisonMap* map = new GarrisonMap(mapId, i_gridCleanUpDelay, instanceId, owner->GetGUID());
144 ASSERT(map->IsGarrison());
145 map->InitSpawnGroupState();
146 return map;
147}
148
149/*
150- return the right instance for the object, based on its InstanceId
151- create the instance if it's not created already
152- the player is not actually added to the instance (only in InstanceMap::Add)
153*/
154Map* MapManager::CreateMap(uint32 mapId, Player* player, Optional<uint32> lfgDungeonsId /*= {}*/)
155{
156 if (!player)
157 return nullptr;
158
159 MapEntry const* entry = sMapStore.LookupEntry(mapId);
160 if (!entry)
161 return nullptr;
162
163 std::scoped_lock lock(_mapsLock);
164
165 Map* map = nullptr;
166 uint32 newInstanceId = 0; // instanceId of the resulting map
167
168 if (entry->IsBattlegroundOrArena())
169 {
170 // instantiate or find existing bg map for player
171 // the instance id is set in battlegroundid
172 newInstanceId = player->GetBattlegroundId();
173 if (!newInstanceId)
174 return nullptr;
175
176 map = FindMap_i(mapId, newInstanceId);
177 if (!map)
178 {
179 if (Battleground* bg = player->GetBattleground())
180 map = CreateBattleground(mapId, newInstanceId, bg);
181 else
182 {
183 player->TeleportToBGEntryPoint();
184 return nullptr;
185 }
186 }
187 }
188 else if (entry->IsDungeon())
189 {
190 Group* group = player->GetGroup();
191 Difficulty difficulty = group ? group->GetDifficultyID(entry) : player->GetDifficultyID(entry);
192 MapDb2Entries entries{ entry, sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty) };
193 ObjectGuid instanceOwnerGuid = group ? group->GetRecentInstanceOwner(mapId) : player->GetGUID();
194 InstanceLock* instanceLock = sInstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, entries);
195 if (instanceLock)
196 {
197 newInstanceId = instanceLock->GetInstanceId();
198
199 // Reset difficulty to the one used in instance lock
200 if (!entries.Map->IsFlexLocking())
201 entries.MapDifficulty = sDB2Manager.GetDownscaledMapDifficultyData(mapId, instanceLock->GetDifficultyId());
202 }
203 else
204 {
205 // Try finding instance id for normal dungeon
206 if (!entries.MapDifficulty->HasResetSchedule())
207 newInstanceId = group ? group->GetRecentInstanceId(mapId) : player->GetRecentInstanceId(mapId);
208
209 // If not found or instance is not a normal dungeon, generate new one
210 if (!newInstanceId)
211 newInstanceId = GenerateInstanceId();
212
213 instanceLock = sInstanceLockMgr.CreateInstanceLockForNewInstance(instanceOwnerGuid, entries, newInstanceId);
214 }
215
216 // it is possible that the save exists but the map doesn't
217 map = FindMap_i(mapId, newInstanceId);
218
219 // is is also possible that instance id is already in use by another group for boss-based locks
220 if (!entries.IsInstanceIdBound() && instanceLock && map && map->ToInstanceMap()->GetInstanceLock() != instanceLock)
221 {
222 newInstanceId = GenerateInstanceId();
223 instanceLock->SetInstanceId(newInstanceId);
224 map = nullptr;
225 }
226
227 if (!map)
228 {
229 map = CreateInstance(mapId, newInstanceId, instanceLock, Difficulty(entries.MapDifficulty->DifficultyID), GetTeamIdForTeam(sCharacterCache->GetCharacterTeamByGuid(instanceOwnerGuid)), group,
230 lfgDungeonsId);
231 if (group)
232 group->SetRecentInstance(mapId, instanceOwnerGuid, newInstanceId);
233 else
234 player->SetRecentInstance(mapId, newInstanceId);
235 }
236 }
237 else if (entry->IsGarrison())
238 {
239 newInstanceId = player->GetGUID().GetCounter();
240 map = FindMap_i(mapId, newInstanceId);
241 if (!map)
242 map = CreateGarrison(mapId, newInstanceId, player);
243 }
244 else
245 {
246 newInstanceId = 0;
247 if (entry->IsSplitByFaction())
248 newInstanceId = player->GetTeamId();
249
250 map = FindMap_i(mapId, newInstanceId);
251 if (!map)
252 map = CreateWorldMap(mapId, newInstanceId);
253 }
254
255 if (map)
256 {
258 if (ptr.get() != map)
259 {
260 ptr.reset(map);
261 map->SetWeakPtr(ptr);
262
263 sScriptMgr->OnCreateMap(map);
264 sOutdoorPvPMgr->CreateOutdoorPvPForMap(map);
265 sBattlefieldMgr->CreateBattlefieldsForMap(map);
266 }
267 }
268
269 return map;
270}
271
272Map* MapManager::FindMap(uint32 mapId, uint32 instanceId) const
273{
274 std::shared_lock<std::shared_mutex> lock(_mapsLock);
275 return FindMap_i(mapId, instanceId);
276}
277
279{
280 MapEntry const* entry = sMapStore.LookupEntry(mapId);
281 if (!entry)
282 return 0;
283
284 if (entry->IsBattlegroundOrArena())
285 return player->GetBattlegroundId();
286 else if (entry->IsDungeon())
287 {
288 Group const* group = player->GetGroup();
289 Difficulty difficulty = group ? group->GetDifficultyID(entry) : player->GetDifficultyID(entry);
290 MapDb2Entries entries{ entry, sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty) };
291 ObjectGuid instanceOwnerGuid = group ? group->GetRecentInstanceOwner(mapId) : player->GetGUID();
292 InstanceLock* instanceLock = sInstanceLockMgr.FindActiveInstanceLock(instanceOwnerGuid, entries);
293 uint32 newInstanceId = 0;
294 if (instanceLock)
295 newInstanceId = instanceLock->GetInstanceId();
296 else if (!entries.MapDifficulty->HasResetSchedule()) // Try finding instance id for normal dungeon
297 newInstanceId = group ? group->GetRecentInstanceId(mapId) : player->GetRecentInstanceId(mapId);
298
299 if (!newInstanceId)
300 return 0;
301
302 Map* map = FindMap(mapId, newInstanceId);
303
304 // is is possible that instance id is already in use by another group for boss-based locks
305 if (!entries.IsInstanceIdBound() && instanceLock && map && map->ToInstanceMap()->GetInstanceLock() != instanceLock)
306 return 0;
307
308 return newInstanceId;
309 }
310 else if (entry->IsGarrison())
311 return uint32(player->GetGUID().GetCounter());
312 else
313 {
314 if (entry->IsSplitByFaction())
315 return player->GetTeamId();
316
317 return 0;
318 }
319}
320
322{
323 i_timer.Update(diff);
324 if (!i_timer.Passed())
325 return;
326
327 MapMapType::iterator iter = i_maps.begin();
328 while (iter != i_maps.end())
329 {
330 if (iter->second->CanUnload(uint32(i_timer.GetCurrent())))
331 {
332 if (DestroyMap(iter->second.get()))
333 iter = i_maps.erase(iter);
334 else
335 ++iter;
336
337 continue;
338 }
339
340 if (m_updater.activated())
342 else
343 iter->second->Update(uint32(i_timer.GetCurrent()));
344
345 ++iter;
346 }
347 if (m_updater.activated())
348 m_updater.wait();
349
350 for (iter = i_maps.begin(); iter != i_maps.end(); ++iter)
351 iter->second->DelayedUpdate(uint32(i_timer.GetCurrent()));
352
354}
355
357{
358 map->RemoveAllPlayers();
359 if (map->HavePlayers())
360 return false;
361
362 sOutdoorPvPMgr->DestroyOutdoorPvPForMap(map);
363 sBattlefieldMgr->DestroyBattlefieldsForMap(map);
364 sScriptMgr->OnDestroyMap(map);
365
366 map->UnloadAll();
367
368 // Free up the instance id and allow it to be reused for normal dungeons, bgs and arenas
369 if (map->IsBattlegroundOrArena() || (map->IsDungeon() && !map->GetMapDifficulty()->HasResetSchedule()))
370 sMapMgr->FreeInstanceId(map->GetInstanceId());
371
372 // erase map
373 return true;
374}
375
377{
378 return sMapStore.LookupEntry(mapId) != nullptr;
379}
380
382{
383 // first unload maps
384 for (auto iter = i_maps.begin(); iter != i_maps.end(); ++iter)
385 {
386 iter->second->UnloadAll();
387
388 sOutdoorPvPMgr->DestroyOutdoorPvPForMap(iter->second.get());
389 sBattlefieldMgr->DestroyBattlefieldsForMap(iter->second.get());
390 sScriptMgr->OnDestroyMap(iter->second.get());
391 }
392
393 // then delete them
394 i_maps.clear();
395
396 if (m_updater.activated())
398
400}
401
403{
404 std::shared_lock<std::shared_mutex> lock(_mapsLock);
405 return std::count_if(i_maps.begin(), i_maps.end(), [](MapMapType::value_type const& value) { return value.second->IsDungeon(); });
406}
407
409{
410 std::shared_lock<std::shared_mutex> lock(_mapsLock);
411 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); });
412}
413
415{
416 _nextInstanceId = 1;
417
418 uint64 maxExistingInstanceId = 0;
419 if (QueryResult result = CharacterDatabase.Query("SELECT IFNULL(MAX(instanceId), 0) FROM instance"))
420 maxExistingInstanceId = std::max(maxExistingInstanceId, (*result)[0].GetUInt64());
421
422 if (QueryResult result = CharacterDatabase.Query("SELECT IFNULL(MAX(instanceId), 0) FROM character_instance_lock"))
423 maxExistingInstanceId = std::max(maxExistingInstanceId, (*result)[0].GetUInt64());
424
425 _freeInstanceIds->resize(maxExistingInstanceId + 2, true); // make space for one extra to be able to access [_nextInstanceId] index in case all slots are taken
426
427 // never allow 0 id
428 _freeInstanceIds->set(0, false);
429}
430
432{
433 // Allocation and sizing was done in InitInstanceIds()
434 _freeInstanceIds->set(instanceId, false);
435
436 // Instances are pulled in ascending order from db and nextInstanceId is initialized with 1,
437 // so if the instance id is used, increment until we find the first unused one for a potential new instance
438 if (_nextInstanceId == instanceId)
440}
441
443{
444 if (_nextInstanceId == 0xFFFFFFFF)
445 {
446 TC_LOG_ERROR("maps", "Instance ID overflow!! Can't continue, shutting down server. ");
448 return _nextInstanceId;
449 }
450
451 uint32 newInstanceId = _nextInstanceId;
452 ASSERT(newInstanceId < _freeInstanceIds->size());
453 _freeInstanceIds->set(newInstanceId, false);
454
455 // Find the lowest available id starting from the current NextInstanceId (which should be the lowest according to the logic in FreeInstanceId())
456 size_t nextFreedId = _freeInstanceIds->find_next(_nextInstanceId++);
457 if (nextFreedId == InstanceIds::npos)
458 {
460 _freeInstanceIds->push_back(true);
461 }
462 else
463 _nextInstanceId = uint32(nextFreedId);
464
465 return newInstanceId;
466}
467
469{
470 // If freed instance id is lower than the next id available for new instances, use the freed one instead
471 _nextInstanceId = std::min(instanceId, _nextInstanceId);
472 _freeInstanceIds->set(instanceId, true);
473}
474
475// hack to allow conditions to access what faction owns the map (these worldstates should not be set on these maps)
477{
478public:
479 SplitByFactionMapScript(char const* name, uint32 mapId) : WorldMapScript(name, mapId)
480 {
481 }
482
488};
489
491{
492 for (MapEntry const* mapEntry : sMapStore)
493 if (mapEntry->IsWorldMap() && mapEntry->IsSplitByFaction())
494 new SplitByFactionMapScript(Trinity::StringFormat("world_map_set_faction_worldstates_{}", mapEntry->ID).c_str(), mapEntry->ID);
495}
#define sBattlefieldMgr
#define sCharacterCache
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:579
Difficulty
Definition DBCEnums.h:935
@ DIFFICULTY_NONE
Definition DBCEnums.h:936
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:79
#define ASSERT
Definition Errors.h:72
#define sInstanceLockMgr
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#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:3481
void SetBG(Battleground *bg)
Definition Map.h:943
BattlegroundScript * GetBattlegroundScript()
Definition Map.h:947
void SetBgMap(BattlegroundMap *map)
static std::string_view GetDifficultyName(Difficulty difficulty)
Definition Group.h:209
uint32 GetRecentInstanceId(uint32 mapId) const
Definition Group.h:394
void SetRecentInstance(uint32 mapId, ObjectGuid instanceOwner, uint32 instanceId)
Definition Group.h:400
ObjectGuid GetRecentInstanceOwner(uint32 mapId) const
Definition Group.h:388
Difficulty GetDifficultyID(MapEntry const *mapEntry) const
Definition Group.cpp:1318
void SetInstanceId(uint32 instanceId)
Difficulty GetDifficultyId() const
bool IsNew() const
uint32 GetInstanceId() const
InstanceLock const * GetInstanceLock() const
Definition Map.h:900
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:227
bool IsDungeon() const
Definition Map.cpp:3303
bool IsBattlegroundOrArena() const
Definition Map.cpp:3405
void LoadAllGrids()
Definition Map.cpp:115
void SetWeakPtr(Trinity::unique_weak_ptr< Map > weakRef)
Definition Map.h:356
MapDifficultyEntry const * GetMapDifficulty() const
Definition Map.cpp:3283
void UnloadAll()
Definition Map.cpp:1662
bool HavePlayers() const
Definition Map.h:397
void LoadRespawnTimes()
Definition Map.cpp:3697
virtual void RemoveAllPlayers()
Definition Map.cpp:1645
static void DeleteStateMachine()
Definition Map.cpp:130
void LoadCorpseData()
Definition Map.cpp:3757
uint32 GetId() const
Definition Map.cpp:3288
InstanceMap * ToInstanceMap()
Definition Map.h:495
void InitSpawnGroupState()
Definition Map.cpp:2500
uint32 GetInstanceId() const
Definition Map.h:353
static void InitStateMachine()
Definition Map.cpp:122
bool IsGarrison() const
Definition Map.cpp:3415
LowType GetCounter() const
Definition ObjectGuid.h:336
void SetRecentInstance(uint32 mapId, uint32 instanceId)
Definition Player.h:2756
uint32 GetRecentInstanceId(uint32 mapId) const
Definition Player.h:2750
TeamId GetTeamId() const
Definition Player.h:2407
bool TeleportToBGEntryPoint()
Definition Player.cpp:1436
Battleground * GetBattleground() const
Definition Player.cpp:25650
uint32 GetBattlegroundId() const
Definition Player.h:2572
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2781
Difficulty GetDifficultyID(MapEntry const *mapEntry) const
Definition Player.cpp:30544
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:670
#define sWorld
Definition World.h:919
@ CONFIG_INTERVAL_GRIDCLEAN
Definition World.h:240
@ CONFIG_INTERVAL_MAPUPDATE
Definition World.h:241
@ CONFIG_NUMTHREADS
Definition World.h:355
@ CONFIG_INSTANCEMAP_LOAD_GRIDS
Definition World.h:181
@ CONFIG_BASEMAP_LOAD_GRIDS
Definition World.h:180
@ CONFIG_BATTLEGROUNDMAP_LOAD_GRIDS
Definition World.h:199
@ 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