aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Maps
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2022-07-24 18:56:40 +0200
committerShauren <shauren.trinity@gmail.com>2022-07-24 18:56:40 +0200
commitfbe0b8efebca3bc2662b477bdf45627f9783d6c2 (patch)
tree6f5662045be0c4e5f06d8a97f9669b7f93cf2df1 /src/server/game/Maps
parent8f2c5413d7a3775a304c793653d41caabf3ba997 (diff)
Core/Maps: Removed MapInstanced - no longer neccessary for grid data reference counting (moved to TerrainInfo)
Diffstat (limited to 'src/server/game/Maps')
-rw-r--r--src/server/game/Maps/Map.cpp272
-rw-r--r--src/server/game/Maps/Map.h15
-rw-r--r--src/server/game/Maps/MapInstanced.cpp311
-rw-r--r--src/server/game/Maps/MapInstanced.h64
-rw-r--r--src/server/game/Maps/MapManager.cpp340
-rw-r--r--src/server/game/Maps/MapManager.h106
-rw-r--r--src/server/game/Maps/TerrainMgr.cpp27
-rw-r--r--src/server/game/Maps/TerrainMgr.h3
-rw-r--r--src/server/game/Maps/TransportMgr.cpp2
9 files changed, 429 insertions, 711 deletions
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 440fe34bc70..1810b63d555 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -32,10 +32,10 @@
#include "GridStates.h"
#include "Group.h"
#include "InstancePackets.h"
+#include "InstanceSaveMgr.h"
#include "InstanceScenario.h"
#include "InstanceScript.h"
#include "Log.h"
-#include "MapInstanced.h"
#include "MapManager.h"
#include "Metric.h"
#include "MiscPackets.h"
@@ -126,7 +126,7 @@ void Map::DeleteStateMachine()
delete si_GridStates[GRID_STATE_REMOVAL];
}
-Map::Map(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, Map* /*_parent*/) :
+Map::Map(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode) :
_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), _areaTriggersToMoveLock(false),
i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
@@ -309,15 +309,9 @@ void Map::DeleteFromWorld(Player* player)
delete player;
}
-void Map::EnsureGridCreated(GridCoord const& p)
-{
- std::lock_guard<std::mutex> lock(_gridLock);
- EnsureGridCreated_i(p);
-}
-
//Create NGrid so the object can be added to it
//But object data is not loaded here
-void Map::EnsureGridCreated_i(GridCoord const& p)
+void Map::EnsureGridCreated(GridCoord const& p)
{
if (!getNGrid(p.x_coord, p.y_coord))
{
@@ -1760,6 +1754,86 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa
return result;
}
+Map::EnterState Map::PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck)
+{
+ MapEntry const* entry = sMapStore.LookupEntry(mapid);
+ if (!entry)
+ return CANNOT_ENTER_NO_ENTRY;
+
+ if (!entry->IsDungeon())
+ return CAN_ENTER;
+
+ Difficulty targetDifficulty, requestedDifficulty;
+ targetDifficulty = requestedDifficulty = player->GetDifficultyID(entry);
+ // Get the highest available difficulty if current setting is higher than the instance allows
+ MapDifficultyEntry const* mapDiff = sDB2Manager.GetDownscaledMapDifficultyData(mapid, targetDifficulty);
+ if (!mapDiff)
+ return CANNOT_ENTER_DIFFICULTY_UNAVAILABLE;
+
+ //Bypass checks for GMs
+ if (player->IsGameMaster())
+ return CAN_ENTER;
+
+ //Other requirements
+ if (!player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true))
+ return CANNOT_ENTER_UNSPECIFIED_REASON;
+
+ char const* mapName = entry->MapName[sWorld->GetDefaultDbcLocale()];
+
+ Group* group = player->GetGroup();
+ if (entry->IsRaid() && entry->Expansion() >= sWorld->getIntConfig(CONFIG_EXPANSION)) // can only enter in a raid group but raids from old expansion don't need a group
+ if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID))
+ return CANNOT_ENTER_NOT_IN_RAID;
+
+ if (!player->IsAlive())
+ {
+ if (player->HasCorpse())
+ {
+ // let enter in ghost mode in instance that connected to inner instance with corpse
+ uint32 corpseMap = player->GetCorpseLocation().GetMapId();
+ do
+ {
+ if (corpseMap == mapid)
+ break;
+
+ InstanceTemplate const* corpseInstance = sObjectMgr->GetInstanceTemplate(corpseMap);
+ corpseMap = corpseInstance ? corpseInstance->Parent : 0;
+ } while (corpseMap);
+
+ if (!corpseMap)
+ return CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE;
+
+ TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName);
+ }
+ else
+ TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str());
+ }
+
+ //Get instance where player's group is bound & its map
+ if (!loginCheck && group)
+ {
+ InstanceGroupBind* boundInstance = group->GetBoundInstance(entry);
+ if (boundInstance && boundInstance->save)
+ if (Map* boundMap = sMapMgr->FindMap(mapid, boundInstance->save->GetInstanceId()))
+ if (EnterState denyReason = boundMap->CannotEnter(player))
+ return denyReason;
+ }
+
+ // players are only allowed to enter 5 instances per hour
+ if (entry->IsDungeon() && (!player->GetGroup() || (player->GetGroup() && !player->GetGroup()->isLFGGroup())))
+ {
+ uint32 instanceIdToCheck = 0;
+ if (InstanceSave* save = player->GetInstanceSave(mapid))
+ instanceIdToCheck = save->GetInstanceId();
+
+ // instanceId can never be 0 - will not be found
+ if (!player->CheckInstanceCount(instanceIdToCheck) && !player->isDead())
+ return CANNOT_ENTER_TOO_MANY_INSTANCES;
+ }
+
+ return CAN_ENTER;
+}
+
char const* Map::GetMapName() const
{
return i_mapEntry->MapName[sWorld->GetDefaultDbcLocale()];
@@ -2695,8 +2769,8 @@ template TC_GAME_API void Map::RemoveFromMap(Conversation*, bool);
/* ******* Dungeon Instance Maps ******* */
-InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, Map* _parent, TeamId InstanceTeam)
- : Map(id, expiry, InstanceId, SpawnMode, _parent),
+InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, TeamId InstanceTeam)
+ : Map(id, expiry, InstanceId, SpawnMode),
m_resetAfterUnload(false), m_unloadWhenEmpty(false),
i_data(nullptr), i_script_id(0), i_script_team(InstanceTeam), i_scenario(nullptr)
{
@@ -2763,112 +2837,97 @@ Map::EnterState InstanceMap::CannotEnter(Player* player)
*/
bool InstanceMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
{
- /// @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode
- // GMs still can teleport player in instance.
- // Is it needed?
-
- {
- std::lock_guard<std::mutex> lock(_mapLock);
- // Check moved to void WorldSession::HandleMoveWorldportAckOpcode()
- //if (!CanEnter(player))
- //return false;
-
- // Dungeon only code
- if (IsDungeon())
- {
- Group* group = player->GetGroup();
+ Group* group = player->GetGroup();
- // increase current instances (hourly limit)
- if (!group || !group->isLFGGroup())
- player->AddInstanceEnterTime(GetInstanceId(), GameTime::GetGameTime());
+ // increase current instances (hourly limit)
+ if (!group || !group->isLFGGroup())
+ player->AddInstanceEnterTime(GetInstanceId(), GameTime::GetGameTime());
- // get or create an instance save for the map
- InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(GetInstanceId());
- if (!mapSave)
- {
- TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetDifficultyID(), GetInstanceId());
- mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), GetDifficultyID(), 0, 0, true);
- }
+ // get or create an instance save for the map
+ InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(GetInstanceId());
+ if (!mapSave)
+ {
+ TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetDifficultyID(), GetInstanceId());
+ mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), GetDifficultyID(), 0, 0, true);
+ }
- ASSERT(mapSave);
+ ASSERT(mapSave);
- // check for existing instance binds
- InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), GetDifficultyID());
- if (playerBind && playerBind->perm)
+ // check for existing instance binds
+ InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), GetDifficultyID());
+ if (playerBind && playerBind->perm)
+ {
+ // cannot enter other instances if bound permanently
+ if (playerBind->save != mapSave)
+ {
+ TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is permanently bound to instance %s %d, %d, %d, %d, %d, %d but he is being put into instance %s %d, %d, %d, %d, %d, %d", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetMapName(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), static_cast<uint32>(playerBind->save->GetDifficultyID()), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset());
+ return false;
+ }
+ }
+ else
+ {
+ if (group)
+ {
+ // solo saves should have been reset when the map was loaded
+ InstanceGroupBind* groupBind = group->GetBoundInstance(this);
+ if (playerBind && playerBind->save != mapSave)
{
- // cannot enter other instances if bound permanently
- if (playerBind->save != mapSave)
- {
- TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is permanently bound to instance %s %d, %d, %d, %d, %d, %d but he is being put into instance %s %d, %d, %d, %d, %d, %d", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetMapName(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), static_cast<uint32>(playerBind->save->GetDifficultyID()), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset());
- return false;
- }
+ TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is being put into instance %s %d, %d, %d, %d, %d, %d but he is in group %s and is bound to instance %d, %d, %d, %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), group->GetLeaderGUID().ToString().c_str(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), static_cast<uint32>(playerBind->save->GetDifficultyID()), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
+ if (groupBind)
+ TC_LOG_ERROR("maps", "InstanceMap::Add: the group is bound to the instance %s %d, %d, %d, %d, %d, %d", GetMapName(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), static_cast<uint32>(groupBind->save->GetDifficultyID()), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
+ //ABORT();
+ return false;
}
+ // bind to the group or keep using the group save
+ if (!groupBind)
+ group->BindToInstance(mapSave, false);
else
{
- if (group)
+ // cannot jump to a different instance without resetting it
+ if (groupBind->save != mapSave)
{
- // solo saves should have been reset when the map was loaded
- InstanceGroupBind* groupBind = group->GetBoundInstance(this);
- if (playerBind && playerBind->save != mapSave)
- {
- TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is being put into instance %s %d, %d, %d, %d, %d, %d but he is in group %s and is bound to instance %d, %d, %d, %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), group->GetLeaderGUID().ToString().c_str(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), static_cast<uint32>(playerBind->save->GetDifficultyID()), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
- if (groupBind)
- TC_LOG_ERROR("maps", "InstanceMap::Add: the group is bound to the instance %s %d, %d, %d, %d, %d, %d", GetMapName(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), static_cast<uint32>(groupBind->save->GetDifficultyID()), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
- //ABORT();
- return false;
- }
- // bind to the group or keep using the group save
- if (!groupBind)
- group->BindToInstance(mapSave, false);
+ TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is being put into instance %d, %d, %d but he is in group %s which is bound to instance %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), group->GetLeaderGUID().ToString().c_str(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), static_cast<uint32>(groupBind->save->GetDifficultyID()));
+ TC_LOG_ERROR("maps", "MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
+ if (groupBind->save)
+ TC_LOG_ERROR("maps", "GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
else
- {
- // cannot jump to a different instance without resetting it
- if (groupBind->save != mapSave)
- {
- TC_LOG_ERROR("maps", "InstanceMap::Add: player %s %s is being put into instance %d, %d, %d but he is in group %s which is bound to instance %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast<uint32>(mapSave->GetDifficultyID()), group->GetLeaderGUID().ToString().c_str(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), static_cast<uint32>(groupBind->save->GetDifficultyID()));
- TC_LOG_ERROR("maps", "MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
- if (groupBind->save)
- TC_LOG_ERROR("maps", "GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
- else
- TC_LOG_ERROR("maps", "GroupBind save NULL");
- return false;
- }
- // if the group/leader is permanently bound to the instance
- // players also become permanently bound when they enter
- if (groupBind->perm)
- {
- WorldPackets::Instance::PendingRaidLock pendingRaidLock;
- pendingRaidLock.TimeUntilLock = 60000;
- pendingRaidLock.CompletedMask = i_data ? i_data->GetCompletedEncounterMask() : 0;
- pendingRaidLock.Extending = false;
- pendingRaidLock.WarningOnly = false; // events it throws: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
- player->SendDirectMessage(pendingRaidLock.Write());
- player->SetPendingBind(mapSave->GetInstanceId(), 60000);
- }
- }
+ TC_LOG_ERROR("maps", "GroupBind save NULL");
+ return false;
}
- else
+ // if the group/leader is permanently bound to the instance
+ // players also become permanently bound when they enter
+ if (groupBind->perm)
{
- // set up a solo bind or continue using it
- if (!playerBind)
- player->BindToInstance(mapSave, false);
- else
- // cannot jump to a different instance without resetting it
- ASSERT(playerBind->save == mapSave);
+ WorldPackets::Instance::PendingRaidLock pendingRaidLock;
+ pendingRaidLock.TimeUntilLock = 60000;
+ pendingRaidLock.CompletedMask = i_data ? i_data->GetCompletedEncounterMask() : 0;
+ pendingRaidLock.Extending = false;
+ pendingRaidLock.WarningOnly = false; // events it throws: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
+ player->SendDirectMessage(pendingRaidLock.Write());
+ player->SetPendingBind(mapSave->GetInstanceId(), 60000);
}
}
}
+ else
+ {
+ // set up a solo bind or continue using it
+ if (!playerBind)
+ player->BindToInstance(mapSave, false);
+ else
+ // cannot jump to a different instance without resetting it
+ ASSERT(playerBind->save == mapSave);
+ }
+ }
- // for normal instances cancel the reset schedule when the
- // first player enters (no players yet)
- SetResetSchedule(false);
+ // for normal instances cancel the reset schedule when the
+ // first player enters (no players yet)
+ SetResetSchedule(false);
- TC_LOG_DEBUG("maps", "MAP: Player '%s' entered instance '%u' of map '%s'", player->GetName().c_str(), GetInstanceId(), GetMapName());
- // initialize unload state
- m_unloadTimer = 0;
- m_resetAfterUnload = false;
- m_unloadWhenEmpty = false;
- }
+ TC_LOG_DEBUG("maps", "MAP: Player '%s' entered instance '%u' of map '%s'", player->GetName().c_str(), GetInstanceId(), GetMapName());
+ // initialize unload state
+ m_unloadTimer = 0;
+ m_resetAfterUnload = false;
+ m_unloadWhenEmpty = false;
// this will acquire the same mutex so it cannot be in the previous block
Map::AddPlayerToMap(player, initPlayer);
@@ -3213,8 +3272,8 @@ uint32 InstanceMap::GetMaxResetDelay() const
/* ******* Battleground Instance Maps ******* */
-BattlegroundMap::BattlegroundMap(uint32 id, time_t expiry, uint32 InstanceId, Map* _parent, Difficulty spawnMode)
- : Map(id, expiry, InstanceId, spawnMode, _parent), m_bg(nullptr)
+BattlegroundMap::BattlegroundMap(uint32 id, time_t expiry, uint32 InstanceId, Difficulty spawnMode)
+ : Map(id, expiry, InstanceId, spawnMode), m_bg(nullptr)
{
//lets initialize visibility distance for BG/Arenas
BattlegroundMap::InitVisibilityDistance();
@@ -3256,14 +3315,7 @@ Map::EnterState BattlegroundMap::CannotEnter(Player* player)
bool BattlegroundMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/)
{
- {
- std::lock_guard<std::mutex> lock(_mapLock);
- //Check moved to void WorldSession::HandleMoveWorldportAckOpcode()
- //if (!CanEnter(player))
- //return false;
- // reset instance validity, battleground maps do not homebind
- player->m_InstanceValid = true;
- }
+ player->m_InstanceValid = true;
return Map::AddPlayerToMap(player, initPlayer);
}
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 471d1b64d5a..ae34acdd23b 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -53,7 +53,6 @@ class InstanceMap;
class InstanceSave;
class InstanceScript;
class InstanceScenario;
-class MapInstanced;
class Object;
class PhaseShift;
class Player;
@@ -145,7 +144,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
{
friend class MapReference;
public:
- Map(uint32 id, time_t, uint32 InstanceId, Difficulty SpawnMode, Map* _parent = nullptr);
+ Map(uint32 id, time_t, uint32 InstanceId, Difficulty SpawnMode);
virtual ~Map();
MapEntry const* GetEntry() const { return i_mapEntry; }
@@ -274,6 +273,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
CANNOT_ENTER_ZONE_IN_COMBAT, // A boss encounter is currently in progress on the target map
CANNOT_ENTER_UNSPECIFIED_REASON
};
+ static EnterState PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck = false);
virtual EnterState CannotEnter(Player* /*player*/) { return CAN_ENTER; }
char const* GetMapName() const;
@@ -402,9 +402,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
return nullptr;
}
- MapInstanced* ToMapInstanced() { if (Instanceable()) return reinterpret_cast<MapInstanced*>(this); return nullptr; }
- MapInstanced const* ToMapInstanced() const { if (Instanceable()) return reinterpret_cast<MapInstanced const*>(this); return nullptr; }
-
InstanceMap* ToInstanceMap() { if (IsDungeon()) return reinterpret_cast<InstanceMap*>(this); else return nullptr; }
InstanceMap const* ToInstanceMap() const { if (IsDungeon()) return reinterpret_cast<InstanceMap const*>(this); return nullptr; }
@@ -538,7 +535,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
bool IsGridLoaded(GridCoord const&) const;
void EnsureGridCreated(GridCoord const&);
- void EnsureGridCreated_i(GridCoord const&);
bool EnsureGridLoaded(Cell const&);
void EnsureGridLoadedForActiveObject(Cell const&, WorldObject const* object);
@@ -561,9 +557,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
protected:
virtual void LoadGridObjects(NGridType* grid, Cell const& cell);
- std::mutex _mapLock;
- std::mutex _gridLock;
-
MapEntry const* i_mapEntry;
Difficulty i_spawnMode;
uint32 i_InstanceId;
@@ -803,7 +796,7 @@ enum InstanceResetMethod
class TC_GAME_API InstanceMap : public Map
{
public:
- InstanceMap(uint32 id, time_t, uint32 InstanceId, Difficulty SpawnMode, Map* _parent, TeamId InstanceTeam);
+ InstanceMap(uint32 id, time_t, uint32 InstanceId, Difficulty SpawnMode, TeamId InstanceTeam);
~InstanceMap();
bool AddPlayerToMap(Player* player, bool initPlayer = true) override;
void RemovePlayerFromMap(Player*, bool) override;
@@ -846,7 +839,7 @@ class TC_GAME_API InstanceMap : public Map
class TC_GAME_API BattlegroundMap : public Map
{
public:
- BattlegroundMap(uint32 id, time_t, uint32 InstanceId, Map* _parent, Difficulty spawnMode);
+ BattlegroundMap(uint32 id, time_t, uint32 InstanceId, Difficulty spawnMode);
~BattlegroundMap();
bool AddPlayerToMap(Player* player, bool initPlayer = true) override;
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
deleted file mode 100644
index 7bb6c9a8460..00000000000
--- a/src/server/game/Maps/MapInstanced.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "MapInstanced.h"
-#include "Battleground.h"
-#include "DB2Stores.h"
-#include "GarrisonMap.h"
-#include "Group.h"
-#include "InstanceSaveMgr.h"
-#include "Log.h"
-#include "MapManager.h"
-#include "MMapFactory.h"
-#include "Player.h"
-#include "ScenarioMgr.h"
-#include "VMapFactory.h"
-#include "VMapManager2.h"
-#include "World.h"
-
-MapInstanced::MapInstanced(uint32 id, time_t expiry) : Map(id, expiry, 0, DIFFICULTY_NORMAL)
-{
-}
-
-MapInstanced::~MapInstanced() = default;
-
-void MapInstanced::InitVisibilityDistance()
-{
- if (m_InstancedMaps.empty())
- return;
- //initialize visibility distances for all instance copies
- for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
- {
- (*i).second->InitVisibilityDistance();
- }
-}
-
-void MapInstanced::Update(uint32 t)
-{
- // take care of loaded GridMaps (when unused, unload it!)
- Map::Update(t);
-
- // update the instanced maps
- InstancedMaps::iterator i = m_InstancedMaps.begin();
-
- while (i != m_InstancedMaps.end())
- {
- if (i->second->CanUnload(t))
- {
- if (!DestroyInstance(i)) // iterator incremented
- {
- //m_unloadTimer
- }
- }
- else
- {
- // update only here, because it may schedule some bad things before delete
- if (sMapMgr->GetMapUpdater()->activated())
- sMapMgr->GetMapUpdater()->schedule_update(*i->second, t);
- else
- i->second->Update(t);
- ++i;
- }
- }
-}
-
-void MapInstanced::DelayedUpdate(uint32 diff)
-{
- for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
- i->second->DelayedUpdate(diff);
-
- Map::DelayedUpdate(diff); // this may be removed
-}
-
-/*
-void MapInstanced::RelocationNotify()
-{
- for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
- i->second->RelocationNotify();
-}
-*/
-
-void MapInstanced::UnloadAll()
-{
- // Unload instanced maps
- for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
- i->second->UnloadAll();
-
- // Delete the maps only after everything is unloaded to prevent crashes
- for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
- delete i->second;
-
- m_InstancedMaps.clear();
-
- // Unload own grids (just dummy(placeholder) grids, neccesary to unload GridMaps!)
- Map::UnloadAll();
-}
-
-/*
-- return the right instance for the object, based on its InstanceId
-- create the instance if it's not created already
-- the player is not actually added to the instance (only in InstanceMap::Add)
-*/
-Map* MapInstanced::CreateInstanceForPlayer(uint32 mapId, Player* player, uint32 loginInstanceId /*= 0*/)
-{
- if (GetId() != mapId || !player)
- return nullptr;
-
- Map* map = nullptr;
- uint32 newInstanceId = 0; // instanceId of the resulting map
-
- if (IsBattlegroundOrArena())
- {
- // instantiate or find existing bg map for player
- // the instance id is set in battlegroundid
- newInstanceId = player->GetBattlegroundId();
- if (!newInstanceId)
- return nullptr;
-
- map = sMapMgr->FindMap(mapId, newInstanceId);
- if (!map)
- {
- if (Battleground* bg = player->GetBattleground())
- map = CreateBattleground(newInstanceId, bg);
- else
- {
- player->TeleportToBGEntryPoint();
- return nullptr;
- }
- }
- }
- else if (IsDungeon())
- {
- InstancePlayerBind* pBind = player->GetBoundInstance(GetId(), player->GetDifficultyID(GetEntry()));
- InstanceSave* pSave = pBind ? pBind->save : nullptr;
-
- // priority:
- // 1. player's permanent bind
- // 2. player's current instance id if this is at login
- // 3. group's current bind
- // 4. player's current bind
- if (!pBind || !pBind->perm)
- {
- if (loginInstanceId) // if the player has a saved instance id on login, we either use this instance or relocate him out (return null)
- {
- map = FindInstanceMap(loginInstanceId);
- if (!map && pSave && pSave->GetInstanceId() == loginInstanceId)
- map = CreateInstance(loginInstanceId, pSave, pSave->GetDifficultyID(), player->GetTeamId());
- return map;
- }
-
- InstanceGroupBind* groupBind = nullptr;
- Group* group = player->GetGroup();
- // use the player's difficulty setting (it may not be the same as the group's)
- if (group)
- {
- groupBind = group->GetBoundInstance(this);
- if (groupBind)
- {
- // solo saves should be reset when entering a group's instance
- player->UnbindInstance(GetId(), player->GetDifficultyID(GetEntry()));
- pSave = groupBind->save;
- }
- }
- }
- if (pSave)
- {
- // solo/perm/group
- newInstanceId = pSave->GetInstanceId();
- map = FindInstanceMap(newInstanceId);
- // it is possible that the save exists but the map doesn't
- if (!map)
- map = CreateInstance(newInstanceId, pSave, pSave->GetDifficultyID(), player->GetTeamId());
- }
- else
- {
- // if no instanceId via group members or instance saves is found
- // the instance will be created for the first time
- newInstanceId = sMapMgr->GenerateInstanceId();
-
- Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficultyID(GetEntry()) : player->GetDifficultyID(GetEntry());
- //Seems it is now possible, but I do not know if it should be allowed
- //ASSERT(!FindInstanceMap(NewInstanceId));
- map = FindInstanceMap(newInstanceId);
- if (!map)
- map = CreateInstance(newInstanceId, nullptr, diff, player->GetTeamId());
- }
- }
- else if (IsGarrison())
- {
- newInstanceId = player->GetGUID().GetCounter();
- map = FindInstanceMap(newInstanceId);
- if (!map)
- map = CreateGarrison(newInstanceId, player);
- }
-
- return map;
-}
-
-InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty, TeamId team)
-{
- // load/create a map
- std::lock_guard<std::mutex> lock(_mapLock);
-
- // make sure we have a valid map id
- MapEntry const* entry = sMapStore.LookupEntry(GetId());
- if (!entry)
- {
- TC_LOG_ERROR("maps", "CreateInstance: no entry for map %d", GetId());
- ABORT();
- }
-
- // some instances only have one difficulty
- sDB2Manager.GetDownscaledMapDifficultyData(GetId(), difficulty);
-
- TC_LOG_DEBUG("maps", "MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %u", save ? "" : "new ", InstanceId, GetId(), static_cast<uint32>(difficulty));
-
- InstanceMap* map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this, team);
- ASSERT(map->IsDungeon());
-
- map->LoadRespawnTimes();
- map->LoadCorpseData();
-
- bool load_data = save != nullptr;
- map->CreateInstanceData(load_data);
- if (InstanceScenario* instanceScenario = sScenarioMgr->CreateInstanceScenario(map, team))
- map->SetInstanceScenario(instanceScenario);
-
- if (sWorld->getBoolConfig(CONFIG_INSTANCEMAP_LOAD_GRIDS))
- map->LoadAllCells();
-
- m_InstancedMaps[InstanceId] = map;
- return map;
-}
-
-BattlegroundMap* MapInstanced::CreateBattleground(uint32 InstanceId, Battleground* bg)
-{
- // load/create a map
- std::lock_guard<std::mutex> lock(_mapLock);
-
- TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg %d for %d created.", InstanceId, GetId());
-
- BattlegroundMap* map = new BattlegroundMap(GetId(), GetGridExpiry(), InstanceId, this, DIFFICULTY_NONE);
- ASSERT(map->IsBattlegroundOrArena());
- map->SetBG(bg);
- bg->SetBgMap(map);
-
- m_InstancedMaps[InstanceId] = map;
- return map;
-}
-
-GarrisonMap* MapInstanced::CreateGarrison(uint32 instanceId, Player* owner)
-{
- std::lock_guard<std::mutex> lock(_mapLock);
-
- GarrisonMap* map = new GarrisonMap(GetId(), GetGridExpiry(), instanceId, this, owner->GetGUID());
- ASSERT(map->IsGarrison());
-
- m_InstancedMaps[instanceId] = map;
- return map;
-}
-
-// increments the iterator after erase
-bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
-{
- itr->second->RemoveAllPlayers();
- if (itr->second->HavePlayers())
- {
- ++itr;
- return false;
- }
-
- itr->second->UnloadAll();
- // should only unload VMaps if this is the last instance and grid unloading is enabled
- if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
- {
- VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
- MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
- // in that case, unload grids of the base map, too
- // so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
- Map::UnloadAll();
- }
-
- // Free up the instance id and allow it to be reused for bgs and arenas (other instances are handled in the InstanceSaveMgr)
- if (itr->second->IsBattlegroundOrArena())
- sMapMgr->FreeInstanceId(itr->second->GetInstanceId());
-
- // erase map
- delete itr->second;
- m_InstancedMaps.erase(itr++);
-
- return true;
-}
-
-Map::EnterState MapInstanced::CannotEnter(Player* /*player*/)
-{
- //ABORT();
- return CAN_ENTER;
-}
diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h
deleted file mode 100644
index 6b2b71ed37e..00000000000
--- a/src/server/game/Maps/MapInstanced.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TRINITY_MAP_INSTANCED_H
-#define TRINITY_MAP_INSTANCED_H
-
-#include "Map.h"
-#include "InstanceSaveMgr.h"
-
-class GarrisonMap;
-
-class TC_GAME_API MapInstanced : public Map
-{
- friend class MapManager;
- public:
- typedef std::unordered_map< uint32, Map*> InstancedMaps;
-
- MapInstanced(uint32 id, time_t expiry);
- MapInstanced(MapInstanced const& right) = delete;
- MapInstanced(MapInstanced&& right) = delete;
- MapInstanced& operator=(MapInstanced const& right) = delete;
- MapInstanced& operator=(MapInstanced&& right) = delete;
- ~MapInstanced();
-
- // functions overwrite Map versions
- void Update(uint32 diff) override;
- void DelayedUpdate(uint32 diff) override;
- //void RelocationNotify();
- void UnloadAll() override;
- EnterState CannotEnter(Player* /*player*/) override;
-
- Map* CreateInstanceForPlayer(uint32 mapId, Player* player, uint32 loginInstanceId = 0);
- Map* FindInstanceMap(uint32 instanceId) const
- {
- InstancedMaps::const_iterator i = m_InstancedMaps.find(instanceId);
- return(i == m_InstancedMaps.end() ? nullptr : i->second);
- }
- bool DestroyInstance(InstancedMaps::iterator &itr);
-
- InstancedMaps &GetInstancedMaps() { return m_InstancedMaps; }
- virtual void InitVisibilityDistance() override;
-
- private:
- InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty, TeamId team);
- BattlegroundMap* CreateBattleground(uint32 InstanceId, Battleground* bg);
- GarrisonMap* CreateGarrison(uint32 instanceId, Player* owner);
-
- InstancedMaps m_InstancedMaps;
-};
-#endif
diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp
index 6d8f5bacdb7..108aad5b3ad 100644
--- a/src/server/game/Maps/MapManager.cpp
+++ b/src/server/game/Maps/MapManager.cpp
@@ -16,22 +16,20 @@
*/
#include "MapManager.h"
-#include "Config.h"
-#include "Corpse.h"
+#include "Battleground.h"
+#include "Containers.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
-#include "GridDefines.h"
+#include "GarrisonMap.h"
#include "Group.h"
#include "InstanceSaveMgr.h"
-#include "InstanceScript.h"
#include "Log.h"
-#include "MapInstanced.h"
-#include "MiscPackets.h"
-#include "ObjectMgr.h"
+#include "Map.h"
#include "Player.h"
-#include "Transport.h"
+#include "ScenarioMgr.h"
#include "World.h"
#include <boost/dynamic_bitset.hpp>
+#include <numeric>
MapManager::MapManager()
: _freeInstanceIds(std::make_unique<InstanceIds>()), _nextInstanceId(0), _scheduledScripts(0)
@@ -40,7 +38,7 @@ MapManager::MapManager()
i_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE));
}
-MapManager::~MapManager() { }
+MapManager::~MapManager() = default;
void MapManager::Initialize()
{
@@ -64,157 +62,195 @@ MapManager* MapManager::instance()
return &instance;
}
-Map* MapManager::CreateBaseMap(uint32 id)
+Map* MapManager::FindMap_i(uint32 mapId, uint32 instanceId) const
{
- Map* map = FindBaseMap(id);
-
- if (!map)
- {
- MapEntry const* entry = sMapStore.AssertEntry(id);
- if (entry->ParentMapID != -1 || entry->CosmeticParentMapID != -1)
- {
- CreateBaseMap(entry->ParentMapID != -1 ? entry->ParentMapID : entry->CosmeticParentMapID);
+ return Trinity::Containers::MapGetValuePtr(i_maps, { mapId, instanceId });
+}
- // must have been created by parent map
- map = FindBaseMap(id);
- return ASSERT_NOTNULL(map);
- }
+Map* MapManager::CreateWorldMap(uint32 mapId, uint32 instanceId)
+{
+ Map* map = new Map(mapId, i_gridCleanUpDelay, instanceId, DIFFICULTY_NONE);
+ map->LoadRespawnTimes();
+ map->LoadCorpseData();
- std::lock_guard<std::mutex> lock(_mapsLock);
- map = CreateBaseMap_i(entry);
- }
+ if (sWorld->getBoolConfig(CONFIG_BASEMAP_LOAD_GRIDS))
+ map->LoadAllCells();
- ASSERT(map);
return map;
}
-Map* MapManager::CreateBaseMap_i(MapEntry const* mapEntry)
+InstanceMap* MapManager::CreateInstance(uint32 mapId, uint32 instanceId, InstanceSave* save, Difficulty difficulty, TeamId team)
{
- Map* map;
- if (mapEntry->Instanceable())
- map = new MapInstanced(mapEntry->ID, i_gridCleanUpDelay);
- else
- map = new Map(mapEntry->ID, i_gridCleanUpDelay, 0, DIFFICULTY_NONE);
-
- i_maps[mapEntry->ID] = map;
-
- if (!mapEntry->Instanceable())
+ // make sure we have a valid map id
+ MapEntry const* entry = sMapStore.LookupEntry(mapId);
+ if (!entry)
{
- map->LoadRespawnTimes();
- map->LoadCorpseData();
+ TC_LOG_ERROR("maps", "CreateInstance: no entry for map %d", mapId);
+ ABORT();
}
+ // some instances only have one difficulty
+ sDB2Manager.GetDownscaledMapDifficultyData(mapId, difficulty);
+
+ TC_LOG_DEBUG("maps", "MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %u", save ? "" : "new ", instanceId, mapId, static_cast<uint32>(difficulty));
+
+ InstanceMap* map = new InstanceMap(mapId, i_gridCleanUpDelay, instanceId, difficulty, team);
+ ASSERT(map->IsDungeon());
+
+ map->LoadRespawnTimes();
+ map->LoadCorpseData();
+
+ bool load_data = save != nullptr;
+ map->CreateInstanceData(load_data);
+ if (InstanceScenario* instanceScenario = sScenarioMgr->CreateInstanceScenario(map, team))
+ map->SetInstanceScenario(instanceScenario);
+
+ if (sWorld->getBoolConfig(CONFIG_INSTANCEMAP_LOAD_GRIDS))
+ map->LoadAllCells();
+
return map;
}
-Map* MapManager::FindBaseNonInstanceMap(uint32 mapId) const
+BattlegroundMap* MapManager::CreateBattleground(uint32 mapId, uint32 instanceId, Battleground* bg)
{
- Map* map = FindBaseMap(mapId);
- if (map && map->Instanceable())
- return nullptr;
+ TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg %d for %d created.", instanceId, mapId);
+
+ BattlegroundMap* map = new BattlegroundMap(mapId, i_gridCleanUpDelay, instanceId, DIFFICULTY_NONE);
+ ASSERT(map->IsBattlegroundOrArena());
+ map->SetBG(bg);
+ bg->SetBgMap(map);
return map;
}
-Map* MapManager::CreateMap(uint32 id, Player* player, uint32 loginInstanceId)
+GarrisonMap* MapManager::CreateGarrison(uint32 mapId, uint32 instanceId, Player* owner)
{
- Map* m = CreateBaseMap(id);
-
- if (m && m->Instanceable())
- m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player, loginInstanceId);
-
- return m;
+ GarrisonMap* map = new GarrisonMap(mapId, i_gridCleanUpDelay, instanceId, owner->GetGUID());
+ ASSERT(map->IsGarrison());
+ return map;
}
-Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const
+/*
+- return the right instance for the object, based on its InstanceId
+- create the instance if it's not created already
+- the player is not actually added to the instance (only in InstanceMap::Add)
+*/
+Map* MapManager::CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId /*= 0*/)
{
- Map* map = FindBaseMap(mapid);
- if (!map)
+ if (!player)
return nullptr;
- if (!map->Instanceable())
- return instanceId == 0 ? map : nullptr;
-
- return ((MapInstanced*)map)->FindInstanceMap(instanceId);
-}
-
-Map::EnterState MapManager::PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck)
-{
- MapEntry const* entry = sMapStore.LookupEntry(mapid);
+ MapEntry const* entry = sMapStore.LookupEntry(mapId);
if (!entry)
- return Map::CANNOT_ENTER_NO_ENTRY;
-
- if (!entry->IsDungeon())
- return Map::CAN_ENTER;
-
- Difficulty targetDifficulty, requestedDifficulty;
- targetDifficulty = requestedDifficulty = player->GetDifficultyID(entry);
- // Get the highest available difficulty if current setting is higher than the instance allows
- MapDifficultyEntry const* mapDiff = sDB2Manager.GetDownscaledMapDifficultyData(mapid, targetDifficulty);
- if (!mapDiff)
- return Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE;
-
- //Bypass checks for GMs
- if (player->IsGameMaster())
- return Map::CAN_ENTER;
-
- //Other requirements
- if (!player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true))
- return Map::CANNOT_ENTER_UNSPECIFIED_REASON;
+ return nullptr;
- char const* mapName = entry->MapName[sWorld->GetDefaultDbcLocale()];
+ std::unique_lock<std::shared_mutex> lock(_mapsLock);
- Group* group = player->GetGroup();
- if (entry->IsRaid() && entry->Expansion() >= sWorld->getIntConfig(CONFIG_EXPANSION)) // can only enter in a raid group but raids from old expansion don't need a group
- if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID))
- return Map::CANNOT_ENTER_NOT_IN_RAID;
+ Map* map = nullptr;
+ uint32 newInstanceId = 0; // instanceId of the resulting map
- if (!player->IsAlive())
+ if (entry->IsBattlegroundOrArena())
{
- if (player->HasCorpse())
+ // instantiate or find existing bg map for player
+ // the instance id is set in battlegroundid
+ newInstanceId = player->GetBattlegroundId();
+ if (!newInstanceId)
+ return nullptr;
+
+ map = FindMap(mapId, newInstanceId);
+ if (!map)
{
- // let enter in ghost mode in instance that connected to inner instance with corpse
- uint32 corpseMap = player->GetCorpseLocation().GetMapId();
- do
+ if (Battleground* bg = player->GetBattleground())
+ map = CreateBattleground(mapId, newInstanceId, bg);
+ else
{
- if (corpseMap == mapid)
- break;
-
- InstanceTemplate const* corpseInstance = sObjectMgr->GetInstanceTemplate(corpseMap);
- corpseMap = corpseInstance ? corpseInstance->Parent : 0;
- } while (corpseMap);
+ player->TeleportToBGEntryPoint();
+ return nullptr;
+ }
+ }
+ }
+ else if (entry->IsDungeon())
+ {
+ InstancePlayerBind* pBind = player->GetBoundInstance(mapId, player->GetDifficultyID(entry));
+ InstanceSave* pSave = pBind ? pBind->save : nullptr;
+
+ // priority:
+ // 1. player's permanent bind
+ // 2. player's current instance id if this is at login
+ // 3. group's current bind
+ // 4. player's current bind
+ if (!pBind || !pBind->perm)
+ {
+ if (loginInstanceId) // if the player has a saved instance id on login, we either use this instance or relocate him out (return null)
+ {
+ map = FindMap_i(mapId, loginInstanceId);
+ if (!map && pSave && pSave->GetInstanceId() == loginInstanceId)
+ map = CreateInstance(mapId, loginInstanceId, pSave, pSave->GetDifficultyID(), player->GetTeamId());
+ return map;
+ }
+
+ InstanceGroupBind* groupBind = nullptr;
+ Group* group = player->GetGroup();
+ // use the player's difficulty setting (it may not be the same as the group's)
+ if (group)
+ {
+ groupBind = group->GetBoundInstance(entry);
+ if (groupBind)
+ {
+ // solo saves should be reset when entering a group's instance
+ player->UnbindInstance(mapId, player->GetDifficultyID(entry));
+ pSave = groupBind->save;
+ }
+ }
+ }
+ if (pSave)
+ {
+ // solo/perm/group
+ newInstanceId = pSave->GetInstanceId();
+ map = FindMap_i(mapId, newInstanceId);
+ // it is possible that the save exists but the map doesn't
+ if (!map)
+ map = CreateInstance(mapId, newInstanceId, pSave, pSave->GetDifficultyID(), player->GetTeamId());
+ }
+ else
+ {
+ Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficultyID(entry) : player->GetDifficultyID(entry);
- if (!corpseMap)
- return Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE;
+ // if no instanceId via group members or instance saves is found
+ // the instance will be created for the first time
+ newInstanceId = GenerateInstanceId();
- TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName);
+ //Seems it is now possible, but I do not know if it should be allowed
+ //ASSERT(!FindInstanceMap(NewInstanceId));
+ map = FindMap_i(mapId, newInstanceId);
+ if (!map)
+ map = CreateInstance(mapId, newInstanceId, nullptr, diff, player->GetTeamId());
}
- else
- TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str());
}
-
- //Get instance where player's group is bound & its map
- if (!loginCheck && group)
+ else if (entry->IsGarrison())
{
- InstanceGroupBind* boundInstance = group->GetBoundInstance(entry);
- if (boundInstance && boundInstance->save)
- if (Map* boundMap = sMapMgr->FindMap(mapid, boundInstance->save->GetInstanceId()))
- if (Map::EnterState denyReason = boundMap->CannotEnter(player))
- return denyReason;
+ newInstanceId = player->GetGUID().GetCounter();
+ map = FindMap_i(mapId, newInstanceId);
+ if (!map)
+ map = CreateGarrison(mapId, newInstanceId, player);
}
-
- // players are only allowed to enter 5 instances per hour
- if (entry->IsDungeon() && (!player->GetGroup() || (player->GetGroup() && !player->GetGroup()->isLFGGroup())))
+ else
{
- uint32 instanceIdToCheck = 0;
- if (InstanceSave* save = player->GetInstanceSave(mapid))
- instanceIdToCheck = save->GetInstanceId();
-
- // instanceId can never be 0 - will not be found
- if (!player->CheckInstanceCount(instanceIdToCheck) && !player->isDead())
- return Map::CANNOT_ENTER_TOO_MANY_INSTANCES;
+ newInstanceId = 0;
+ map = FindMap_i(mapId, newInstanceId);
+ if (!map)
+ map = CreateWorldMap(mapId, newInstanceId);
}
- return Map::CAN_ENTER;
+ if (map)
+ i_maps[{ map->GetId(), map->GetInstanceId() }] = map;
+
+ return map;
+}
+
+Map* MapManager::FindMap(uint32 mapId, uint32 instanceId) const
+{
+ std::shared_lock<std::shared_mutex> lock(_mapsLock);
+ return FindMap_i(mapId, instanceId);
}
void MapManager::Update(uint32 diff)
@@ -224,12 +260,24 @@ void MapManager::Update(uint32 diff)
return;
MapMapType::iterator iter = i_maps.begin();
- for (; iter != i_maps.end(); ++iter)
+ while (iter != i_maps.end())
{
+ if (iter->second->CanUnload(diff))
+ {
+ if (DestroyMap(iter->second))
+ iter = i_maps.erase(iter);
+ else
+ ++iter;
+
+ continue;
+ }
+
if (m_updater.activated())
m_updater.schedule_update(*iter->second, uint32(i_timer.GetCurrent()));
else
iter->second->Update(uint32(i_timer.GetCurrent()));
+
+ ++iter;
}
if (m_updater.activated())
m_updater.wait();
@@ -240,7 +288,22 @@ void MapManager::Update(uint32 diff)
i_timer.SetCurrent(0);
}
-void MapManager::DoDelayedMovesAndRemoves() { }
+bool MapManager::DestroyMap(Map* map)
+{
+ map->RemoveAllPlayers();
+ if (map->HavePlayers())
+ return false;
+
+ map->UnloadAll();
+
+ // Free up the instance id and allow it to be reused for bgs and arenas (other instances are handled in the InstanceSaveMgr)
+ if (map->IsBattlegroundOrArena())
+ sMapMgr->FreeInstanceId(map->GetInstanceId());
+
+ // erase map
+ delete map;
+ return true;
+}
bool MapManager::IsValidMAP(uint32 mapId)
{
@@ -265,37 +328,16 @@ void MapManager::UnloadAll()
Map::DeleteStateMachine();
}
-uint32 MapManager::GetNumInstances()
+uint32 MapManager::GetNumInstances() const
{
- std::lock_guard<std::mutex> lock(_mapsLock);
-
- uint32 ret = 0;
- for (MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
- {
- Map* map = itr->second;
- if (!map->IsDungeon())
- continue;
- MapInstanced::InstancedMaps &maps = ((MapInstanced*)map)->GetInstancedMaps();
- ret += maps.size();
- }
- return ret;
+ std::shared_lock<std::shared_mutex> lock(_mapsLock);
+ return std::count_if(i_maps.begin(), i_maps.end(), [](MapMapType::value_type const& value) { return value.second->IsDungeon(); });
}
-uint32 MapManager::GetNumPlayersInInstances()
+uint32 MapManager::GetNumPlayersInInstances() const
{
- std::lock_guard<std::mutex> lock(_mapsLock);
-
- uint32 ret = 0;
- for (MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
- {
- Map* map = itr->second;
- if (!map->IsDungeon())
- continue;
- MapInstanced::InstancedMaps &maps = ((MapInstanced*)map)->GetInstancedMaps();
- for (MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
- ret += mitr->second->GetPlayers().getSize();
- }
- return ret;
+ std::shared_lock<std::shared_mutex> lock(_mapsLock);
+ 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().getSize() : 0); });
}
void MapManager::InitInstanceIds()
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index f2c3bbb1e86..8fb598dd8c2 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -18,27 +18,42 @@
#ifndef TRINITY_MAPMANAGER_H
#define TRINITY_MAPMANAGER_H
-#include "Map.h"
-#include "MapInstanced.h"
-#include "GridStates.h"
+#include "GridDefines.h"
+#include "IteratorPair.h"
#include "MapUpdater.h"
+#include "Position.h"
+#include "SharedDefines.h"
#include <boost/dynamic_bitset_fwd.hpp>
-
-class PhaseShift;
-class Transport;
+#include <map>
+#include <shared_mutex>
+
+class Battleground;
+class BattlegroundMap;
+class GarrisonMap;
+class InstanceMap;
+class InstanceSave;
+class Map;
+class Player;
+enum Difficulty : uint8;
class TC_GAME_API MapManager
{
+ MapManager();
+ ~MapManager();
+
public:
+ MapManager(MapManager const&) = delete;
+ MapManager(MapManager&&) = delete;
+ MapManager& operator=(MapManager const&) = delete;
+ MapManager& operator=(MapManager&&) = delete;
+
static MapManager* instance();
- Map* CreateBaseMap(uint32 mapId);
- Map* FindBaseNonInstanceMap(uint32 mapId) const;
- Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId=0);
+ Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId = 0);
Map* FindMap(uint32 mapId, uint32 instanceId) const;
void Initialize();
- void Update(uint32);
+ void Update(uint32 diff);
void SetGridCleanUpDelay(uint32 t)
{
@@ -57,7 +72,6 @@ class TC_GAME_API MapManager
i_timer.Reset();
}
- //void LoadGrid(int mapid, int instId, float x, float y, WorldObject const* obj, bool no_unload = false);
void UnloadAll();
static bool IsValidMAP(uint32 mapId);
@@ -87,14 +101,11 @@ class TC_GAME_API MapManager
return IsValidMapCoord(loc.GetMapId(), loc);
}
- void DoDelayedMovesAndRemoves();
-
- Map::EnterState PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck = false);
void InitializeVisibilityDistanceInfo();
/* statistics */
- uint32 GetNumInstances();
- uint32 GetNumPlayersInInstances();
+ uint32 GetNumInstances() const;
+ uint32 GetNumPlayersInInstances() const;
// Instance ID management
void InitInstanceIds();
@@ -116,24 +127,20 @@ class TC_GAME_API MapManager
bool IsScriptScheduled() const { return _scheduledScripts > 0; }
private:
- typedef std::unordered_map<uint32, Map*> MapMapType;
+ using MapKey = std::pair<uint32, uint32>;
+ typedef std::map<MapKey, Map*> MapMapType;
typedef boost::dynamic_bitset<size_t> InstanceIds;
- MapManager();
- ~MapManager();
+ Map* FindMap_i(uint32 mapId, uint32 instanceId) const;
- Map* FindBaseMap(uint32 mapId) const
- {
- MapMapType::const_iterator iter = i_maps.find(mapId);
- return (iter == i_maps.end() ? nullptr : iter->second);
- }
+ Map* CreateWorldMap(uint32 mapId, uint32 instanceId);
+ InstanceMap* CreateInstance(uint32 mapId, uint32 instanceId, InstanceSave* save, Difficulty difficulty, TeamId team);
+ BattlegroundMap* CreateBattleground(uint32 mapId, uint32 instanceId, Battleground* bg);
+ GarrisonMap* CreateGarrison(uint32 mapId, uint32 instanceId, Player* owner);
- Map* CreateBaseMap_i(MapEntry const* mapEntry);
+ bool DestroyMap(Map* map);
- MapManager(MapManager const&) = delete;
- MapManager& operator=(MapManager const&) = delete;
-
- std::mutex _mapsLock;
+ mutable std::shared_mutex _mapsLock;
uint32 i_gridCleanUpDelay;
MapMapType i_maps;
IntervalTimer i_timer;
@@ -149,41 +156,26 @@ class TC_GAME_API MapManager
template<typename Worker>
void MapManager::DoForAllMaps(Worker&& worker)
{
- std::lock_guard<std::mutex> lock(_mapsLock);
+ std::shared_lock<std::shared_mutex> lock(_mapsLock);
- for (auto& mapPair : i_maps)
- {
- Map* map = mapPair.second;
- if (MapInstanced* mapInstanced = map->ToMapInstanced())
- {
- MapInstanced::InstancedMaps& instances = mapInstanced->GetInstancedMaps();
- for (auto& instancePair : instances)
- worker(instancePair.second);
- }
- else
- worker(map);
- }
+ for (auto const& [key, map] : i_maps)
+ worker(map);
}
template<typename Worker>
-inline void MapManager::DoForAllMapsWithMapId(uint32 mapId, Worker&& worker)
+void MapManager::DoForAllMapsWithMapId(uint32 mapId, Worker&& worker)
{
- std::lock_guard<std::mutex> lock(_mapsLock);
+ std::shared_lock<std::shared_mutex> lock(_mapsLock);
- auto itr = i_maps.find(mapId);
- if (itr != i_maps.end())
- {
- Map* map = itr->second;
- if (MapInstanced* mapInstanced = map->ToMapInstanced())
- {
- MapInstanced::InstancedMaps& instances = mapInstanced->GetInstancedMaps();
- for (auto& p : instances)
- worker(p.second);
- }
- else
- worker(map);
- }
+ auto range = Trinity::Containers::MakeIteratorPair(
+ i_maps.lower_bound({ mapId, 0 }),
+ i_maps.upper_bound({ mapId, std::numeric_limits<uint32>::max() })
+ );
+
+ for (auto const& [key, map] : range)
+ worker(map);
}
#define sMapMgr MapManager::instance()
+
#endif
diff --git a/src/server/game/Maps/TerrainMgr.cpp b/src/server/game/Maps/TerrainMgr.cpp
index d1babe99ae1..e41cadf4808 100644
--- a/src/server/game/Maps/TerrainMgr.cpp
+++ b/src/server/game/Maps/TerrainMgr.cpp
@@ -35,7 +35,11 @@ TerrainInfo::TerrainInfo(uint32 mapId) : _mapId(mapId), _parentTerrain(nullptr),
{
}
-TerrainInfo::~TerrainInfo() = default;
+TerrainInfo::~TerrainInfo()
+{
+ VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId());
+}
char const* TerrainInfo::GetMapName() const
{
@@ -69,7 +73,7 @@ void TerrainInfo::DiscoverGridMapFiles()
for (uint32 gx = 0; gx < MAX_NUMBER_OF_GRIDS; ++gx)
for (uint32 gy = 0; gy < MAX_NUMBER_OF_GRIDS; ++gy)
- _gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = ExistMap(GetId(), gx, gy, false);
+ _gridFileExists[GetBitsetIndex(gx, gy)] = ExistMap(GetId(), gx, gy, false);
}
bool TerrainInfo::ExistMap(uint32 mapid, int32 gx, int32 gy, bool log /*= true*/)
@@ -144,7 +148,7 @@ bool TerrainInfo::ExistVMap(uint32 mapid, int32 gx, int32 gy)
bool TerrainInfo::HasChildTerrainGridFile(uint32 mapId, int32 gx, int32 gy) const
{
auto childMapItr = std::find_if(_childTerrain.begin(), _childTerrain.end(), [mapId](std::shared_ptr<TerrainInfo> const& childTerrain) { return childTerrain->GetId() == mapId; });
- return childMapItr != _childTerrain.end() && (*childMapItr)->_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy];
+ return childMapItr != _childTerrain.end() && (*childMapItr)->_gridFileExists[GetBitsetIndex(gx, gy)];
}
void TerrainInfo::AddChildTerrain(std::shared_ptr<TerrainInfo> childTerrain)
@@ -170,6 +174,8 @@ void TerrainInfo::LoadMapAndVMapImpl(int32 gx, int32 gy)
for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain)
childTerrain->LoadMapAndVMapImpl(gx, gy);
+
+ _loadedGrids[GetBitsetIndex(gx, gy)] = true;
}
void TerrainInfo::LoadMap(int32 gx, int32 gy)
@@ -177,7 +183,7 @@ void TerrainInfo::LoadMap(int32 gx, int32 gy)
if (_gridMap[gx][gy])
return;
- if (!_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy])
+ if (!_gridFileExists[GetBitsetIndex(gx, gy)])
return;
// map file name
@@ -189,7 +195,7 @@ void TerrainInfo::LoadMap(int32 gx, int32 gy)
if (gridMapLoadResult == GridMap::LoadResult::Ok)
_gridMap[gx][gy] = std::move(gridMap);
else
- _gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = false;
+ _gridFileExists[GetBitsetIndex(gx, gy)] = false;
if (gridMapLoadResult == GridMap::LoadResult::InvalidFile)
TC_LOG_ERROR("maps", "Error loading map file: %s", fileName.c_str());
@@ -206,12 +212,15 @@ void TerrainInfo::LoadVMap(int32 gx, int32 gy)
case VMAP::LoadResult::Success:
TC_LOG_DEBUG("maps", "VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
- default:
+ case VMAP::LoadResult::VersionMismatch:
+ case VMAP::LoadResult::ReadFromFileFailed:
TC_LOG_ERROR("maps", "Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
case VMAP::LoadResult::DisabledInConfig:
TC_LOG_DEBUG("maps", "Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
+ default:
+ break;
}
}
@@ -242,6 +251,8 @@ void TerrainInfo::UnloadMapImpl(int32 gx, int32 gy)
for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain)
childTerrain->UnloadMapImpl(gx, gy);
+
+ _loadedGrids[GetBitsetIndex(gx, gy)] = false;
}
GridMap* TerrainInfo::GetGrid(uint32 mapId, float x, float y, bool loadIfMissing /*= true*/)
@@ -251,7 +262,7 @@ GridMap* TerrainInfo::GetGrid(uint32 mapId, float x, float y, bool loadIfMissing
int32 gy = (int)(CENTER_GRID_ID - y / SIZE_OF_GRIDS); //grid y
// ensure GridMap is loaded
- if (!_gridMap[gx][gy] && loadIfMissing)
+ if (!_loadedGrids[GetBitsetIndex(gx, gy)] && loadIfMissing)
{
std::lock_guard<std::mutex> lock(_loadMutex);
LoadMapAndVMapImpl(gx, gy);
@@ -277,7 +288,7 @@ void TerrainInfo::CleanUpGrids(uint32 diff)
// delete those GridMap objects which have refcount = 0
for (int32 x = 0; x < MAX_NUMBER_OF_GRIDS; ++x)
for (int32 y = 0; y < MAX_NUMBER_OF_GRIDS; ++y)
- if (_gridMap[x][y] && !_referenceCountFromMap[x][y])
+ if (_loadedGrids[GetBitsetIndex(x, y)] && !_referenceCountFromMap[x][y])
UnloadMapImpl(x, y);
_cleanupTimer.Reset(CleanupInterval);
diff --git a/src/server/game/Maps/TerrainMgr.h b/src/server/game/Maps/TerrainMgr.h
index 0bf2ab35a05..54b9192109b 100644
--- a/src/server/game/Maps/TerrainMgr.h
+++ b/src/server/game/Maps/TerrainMgr.h
@@ -97,6 +97,8 @@ public:
float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 2.03128f, DynamicMapTree const* dynamicMapTree = nullptr); // DEFAULT_COLLISION_HEIGHT in Object.h
private:
+ static constexpr int32 GetBitsetIndex(int32 gx, int32 gy) { return gx * MAX_NUMBER_OF_GRIDS + gy; }
+
uint32 _mapId;
TerrainInfo* _parentTerrain;
@@ -105,6 +107,7 @@ private:
std::mutex _loadMutex;
std::unique_ptr<GridMap> _gridMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
std::atomic<uint16> _referenceCountFromMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
+ std::bitset<MAX_NUMBER_OF_GRIDS* MAX_NUMBER_OF_GRIDS> _loadedGrids;
std::bitset<MAX_NUMBER_OF_GRIDS* MAX_NUMBER_OF_GRIDS> _gridFileExists; // cache what grids are available for this map (not including parent/child maps)
static constexpr Milliseconds CleanupInterval = 1min;
diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp
index f0e32ce98d6..59835a201f5 100644
--- a/src/server/game/Maps/TransportMgr.cpp
+++ b/src/server/game/Maps/TransportMgr.cpp
@@ -21,7 +21,7 @@
#include "DB2Stores.h"
#include "InstanceScript.h"
#include "Log.h"
-#include "MapManager.h"
+#include "Map.h"
#include "MoveSplineInitArgs.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"