diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 677b5ec6c8..26f70dc79c 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -28,12 +28,10 @@ #include "GroupMgr.h" #include "Log.h" #include "Map.h" -#include "MapManager.h" #include "MiscPackets.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "WorldPacket.h" -#include "WorldSession.h" #include Battlefield::Battlefield(Map* map) diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 39e88f8c34..686e400ca0 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -39,7 +39,6 @@ #include "Formulas.h" #include "GameEventMgr.h" #include "Language.h" -#include "Map.h" #include "MapManager.h" #include "SharedDefines.h" #include "ObjectMgr.h" diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7ce3878ccd..984ccc4677 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -68,7 +68,6 @@ #include "LootMgr.h" #include "LootPackets.h" #include "Mail.h" -#include "MapInstanced.h" #include "MapManager.h" #include "MiscPackets.h" #include "MotionMaster.h" @@ -1536,7 +1535,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // Check enter rights before map getting to avoid creating instance copy for player // this check not dependent from map instance copy and same for all instance copies of selected map - if (sMapMgr->PlayerCannotEnter(mapid, this, false)) + if (Map::PlayerCannotEnter(mapid, this, false)) return false; //I think this always returns true. Correct me if I am wrong. diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index d140c04420..734f445981 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -24,7 +24,6 @@ #include "GameObjectAI.h" #include "GameTime.h" #include "Log.h" -#include "MapManager.h" #include "ObjectMgr.h" #include "PhasingHandler.h" #include "Player.h" diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 3cf98c9f97..321ac52d27 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -26,6 +26,7 @@ #include "GameTime.h" #include "Language.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "ObjectMgr.h" #include "Player.h" diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 8a6cb3c348..2290c9c425 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -41,8 +41,10 @@ #include "GuildMgr.h" #include "InspectPackets.h" #include "InstanceScript.h" +#include "InstanceSaveMgr.h" #include "Language.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "MiscPackets.h" #include "Object.h" @@ -702,7 +704,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recvData) bool teleported = false; if (player->GetMapId() != at->target_mapId) { - if (Map::EnterState denyReason = sMapMgr->PlayerCannotEnter(at->target_mapId, player, false)) + if (Map::EnterState denyReason = Map::PlayerCannotEnter(at->target_mapId, player, false)) { bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance? switch (denyReason) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index d9200ca060..c4d8e784c3 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -21,6 +21,7 @@ #include "ObjectAccessor.h" #include "Opcodes.h" #include "Log.h" +#include "Map.h" #include "Corpse.h" #include "DBCStores.h" #include "Player.h" diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index f469c91d7a..d78d34ad19 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -27,7 +27,6 @@ #include "InstanceScript.h" #include "Log.h" #include "Map.h" -#include "MapInstanced.h" #include "MapManager.h" #include "ObjectMgr.h" #include "Player.h" @@ -597,7 +596,7 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId) { TC_LOG_DEBUG("maps", "InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId); - Map const* map = sMapMgr->CreateBaseMap(mapid); + MapEntry const* map = sMapStore.LookupEntry(mapid); if (!map->IsDungeon()) return; @@ -607,7 +606,7 @@ void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId) DeleteInstanceFromDB(instanceId); // even if save not loaded - Map* iMap = ((MapInstanced*)map)->FindInstanceMap(instanceId); + Map* iMap = sMapMgr->FindMap(mapid, instanceId); if (iMap) { @@ -687,28 +686,23 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b } // note: this isn't fast but it's meant to be executed very rarely - Map const* map = sMapMgr->CreateBaseMap(mapid); // _not_ include difficulty - MapInstanced::InstancedMaps &instMaps = ((MapInstanced*)map)->GetInstancedMaps(); - MapInstanced::InstancedMaps::iterator mitr; - uint32 timeLeft; - - for (mitr = instMaps.begin(); mitr != instMaps.end(); ++mitr) + if (mapEntry->IsDungeon()) { - Map* map2 = mitr->second; - if (!map2->IsDungeon()) - continue; - - if (warn) + sMapMgr->DoForAllMapsWithMapId(mapid, [=](Map* map) { - if (now >= resetTime) - timeLeft = 0; - else - timeLeft = uint32(resetTime - now); + if (warn) + { + uint32 timeLeft; + if (now >= resetTime) + timeLeft = 0; + else + timeLeft = uint32(resetTime - now); - ((InstanceMap*)map2)->SendResetWarnings(timeLeft); - } - else - ((InstanceMap*)map2)->Reset(INSTANCE_RESET_GLOBAL); + ((InstanceMap*)map)->SendResetWarnings(timeLeft); + } + else + ((InstanceMap*)map)->Reset(INSTANCE_RESET_GLOBAL); + }); } /// @todo delete creature/gameobject respawn times even if the maps are not loaded diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index f23d7ea220..b7dfa9d9c9 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -31,8 +31,8 @@ #include "Group.h" #include "InstanceScript.h" #include "InstancePackets.h" +#include "InstanceSaveMgr.h" #include "Log.h" -#include "MapInstanced.h" #include "MapManager.h" #include "MMapFactory.h" #include "MiscPackets.h" @@ -121,7 +121,7 @@ void Map::DeleteStateMachine() delete si_GridStates[GRID_STATE_REMOVAL]; } -Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* /*_parent*/) : +Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) : _creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), @@ -336,15 +336,9 @@ void Map::DeleteFromWorld(Player* player) delete player; } -void Map::EnsureGridCreated(const GridCoord &p) -{ - std::lock_guard 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(const GridCoord &p) +void Map::EnsureGridCreated(GridCoord const& p) { if (!getNGrid(p.x_coord, p.y_coord)) { @@ -1594,6 +1588,87 @@ 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->GetDifficulty(entry->IsRaid()); + // Get the highest available difficulty if current setting is higher than the instance allows + MapDifficulty const* mapDiff = sDBCManager.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; + + 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, entry->IsRaid())) + 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; @@ -2507,8 +2582,8 @@ template TC_GAME_API void Map::RemoveFromMap(AreaTrigger*, bool); /* ******* Dungeon Instance Maps ******* */ -InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent, TeamId InstanceTeam) - : Map(id, expiry, InstanceId, SpawnMode, _parent), +InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 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) { @@ -2575,113 +2650,98 @@ Map::EnterState InstanceMap::CannotEnter(Player* player) */ bool InstanceMap::AddPlayerToMap(Player* player) { - /// @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode - // GMs still can teleport player in instance. - // Is it needed? + Group* group = player->GetGroup(); + // 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) { - std::lock_guard lock(_mapLock); - // Check moved to void WorldSession::HandleMoveWorldportAckOpcode() - //if (!CanEnter(player)) - //return false; + TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetDifficulty(), GetInstanceId()); + mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), GetDifficulty(), 0, 0, true); + } - // Dungeon only code - if (IsDungeon()) + ASSERT(mapSave); + + // check for existing instance binds + InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), GetDifficulty()); + if (playerBind && playerBind->perm) + { + // cannot enter other instances if bound permanently + if (playerBind->save != mapSave) { - Group* group = player->GetGroup(); - - // 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_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(playerBind->save->GetDifficulty()), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), static_cast(mapSave->GetDifficulty()), 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) { - TC_LOG_DEBUG("maps", "InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); - mapSave = sInstanceSaveMgr->AddInstanceSave(GetId(), GetInstanceId(), Difficulty(GetSpawnMode()), 0, true); - } - - ASSERT(mapSave); - - // check for existing instance binds - InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), Difficulty(GetSpawnMode())); - if (playerBind && playerBind->perm) - { - // cannot enter other instances if bound permanently - if (playerBind->save != mapSave) - { - TC_LOG_ERROR("maps", "InstanceMap::Add: player %s(%d) 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().GetCounter(), GetMapName(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), 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(mapSave->GetDifficulty()), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), group->GetLeaderGUID().ToString().c_str(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), static_cast(playerBind->save->GetDifficulty()), 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(groupBind->save->GetDifficulty()), 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(%d) 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().GetCounter(), GetMapName(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), group->GetLeaderGUID().ToString().c_str(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), 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(), groupBind->save->GetDifficulty(), 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(mapSave->GetDifficulty()), group->GetLeaderGUID().ToString().c_str(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), static_cast(groupBind->save->GetDifficulty())); + 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(%d) 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().GetCounter(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), group->GetLeaderGUID().ToString().c_str(), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); - 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 nullptr"); - 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); } } } - - // 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; + 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); + + 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); @@ -2990,8 +3050,8 @@ uint32 InstanceMap::GetMaxResetDelay() const /* ******* Battleground Instance Maps ******* */ -BattlegroundMap::BattlegroundMap(uint32 id, time_t expiry, uint32 InstanceId, Map* _parent, uint8 spawnMode) - : Map(id, expiry, InstanceId, spawnMode, _parent), m_bg(nullptr) +BattlegroundMap::BattlegroundMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 spawnMode) + : Map(id, expiry, InstanceId, spawnMode), m_bg(nullptr) { //lets initialize visibility distance for BG/Arenas BattlegroundMap::InitVisibilityDistance(); @@ -3033,14 +3093,7 @@ Map::EnterState BattlegroundMap::CannotEnter(Player* player) bool BattlegroundMap::AddPlayerToMap(Player* player) { - { - std::lock_guard 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); } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 232386709f..382ab5fc29 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -47,7 +47,6 @@ class Group; class InstanceMap; class InstanceSave; class InstanceScript; -class MapInstanced; class Object; class PhaseShift; class Player; @@ -166,7 +165,7 @@ class TC_GAME_API Map : public GridRefManager { friend class MapReference; public: - Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent = nullptr); + Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); virtual ~Map(); MapEntry const* GetEntry() const { return i_mapEntry; } @@ -294,6 +293,7 @@ class TC_GAME_API Map : public GridRefManager 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 return nullptr; } - MapInstanced* ToMapInstanced() { if (Instanceable()) return reinterpret_cast(this); return nullptr; } - MapInstanced const* ToMapInstanced() const { if (Instanceable()) return reinterpret_cast(this); return nullptr; } - InstanceMap* ToInstanceMap() { if (IsDungeon()) return reinterpret_cast(this); else return nullptr; } InstanceMap const* ToInstanceMap() const { if (IsDungeon()) return reinterpret_cast(this); return nullptr; } @@ -519,9 +516,9 @@ class TC_GAME_API Map : public GridRefManager bool _dynamicObjectsToMoveLock; std::vector _dynamicObjectsToMove; - bool IsGridLoaded(const GridCoord &) const; - void EnsureGridCreated(const GridCoord &); - void EnsureGridCreated_i(const GridCoord &); + bool IsGridLoaded(GridCoord const&) const; + void EnsureGridCreated(GridCoord const&); + void EnsureGridCreated_i(GridCoord const&); bool EnsureGridLoaded(Cell const&); void EnsureGridLoadedForActiveObject(Cell const&, WorldObject* object); @@ -542,8 +539,6 @@ class TC_GAME_API Map : public GridRefManager void SendObjectUpdates(); protected: - std::mutex _mapLock; - std::mutex _gridLock; MapEntry const* i_mapEntry; uint8 i_spawnMode; @@ -768,7 +763,7 @@ enum InstanceResetMethod class TC_GAME_API InstanceMap : public Map { public: - InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent, TeamId InstanceTeam); + InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, TeamId InstanceTeam); ~InstanceMap(); bool AddPlayerToMap(Player*) override; void RemovePlayerFromMap(Player*, bool) override; @@ -805,7 +800,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, uint8 spawnMode); + BattlegroundMap(uint32 id, time_t, uint32 InstanceId, uint8 spawnMode); ~BattlegroundMap(); bool AddPlayerToMap(Player*) override; diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp deleted file mode 100644 index 129cc2d58b..0000000000 --- a/src/server/game/Maps/MapInstanced.cpp +++ /dev/null @@ -1,303 +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 . - */ - -#include "MapInstanced.h" -#include "Battleground.h" -#include "DBCStores.h" -#include "Group.h" -#include "InstanceSaveMgr.h" -#include "Log.h" -#include "MapManager.h" -#include "MMapFactory.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "VMapFactory.h" -#include "VMapManager2.h" -#include "World.h" - -MapInstanced::MapInstanced(uint32 id, time_t expiry) : Map(id, expiry, 0, DUNGEON_DIFFICULTY_NORMAL) -{ -} - -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) -{ - 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->GetDifficulty(IsRaid())); - 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->GetDifficulty(), 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->GetDifficulty(IsRaid())); - 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->GetDifficulty(), 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()->GetDifficulty(IsRaid()) : player->GetDifficulty(IsRaid()); - //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()); - } - } - - return map; -} - -InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty, TeamId InstanceTeam) -{ - // load/create a map - std::lock_guard 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(); - } - InstanceTemplate const* iTemplate = sObjectMgr->GetInstanceTemplate(GetId()); - if (!iTemplate) - { - TC_LOG_ERROR("maps", "CreateInstance: no instance template for map %d", GetId()); - ABORT(); - } - - // some instances only have one difficulty - sDBCManager.GetDownscaledMapDifficultyData(GetId(), difficulty); - - TC_LOG_DEBUG("maps", "MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); - - InstanceMap* map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this, InstanceTeam); - ASSERT(map->IsDungeon()); - - map->LoadRespawnTimes(); - map->LoadCorpseData(); - - bool load_data = save != nullptr; - map->CreateInstanceData(load_data); - - 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 lock(_mapLock); - - TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg %d for %d created.", InstanceId, GetId()); - - PvPDifficultyEntry const* bracketEntry = sDBCManager.GetBattlegroundBracketByLevel(bg->GetMapId(), bg->GetMinLevel()); - - uint8 spawnMode; - - if (bracketEntry) - spawnMode = bracketEntry->Difficulty; - else - spawnMode = REGULAR_DIFFICULTY; - - BattlegroundMap* map = new BattlegroundMap(GetId(), GetGridExpiry(), InstanceId, this, spawnMode); - ASSERT(map->IsBattlegroundOrArena()); - map->SetBG(bg); - bg->SetBgMap(map); - - 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 394f9c245f..0000000000 --- a/src/server/game/Maps/MapInstanced.h +++ /dev/null @@ -1,58 +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 . - */ - -#ifndef TRINITY_MAP_INSTANCED_H -#define TRINITY_MAP_INSTANCED_H - -#include "Map.h" -#include "InstanceSaveMgr.h" -#include "DBCEnums.h" - -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() { } - - // functions overwrite Map versions - void Update(uint32) 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 InstanceTeam); - BattlegroundMap* CreateBattleground(uint32 InstanceId, Battleground* bg); - - InstancedMaps m_InstancedMaps; -}; -#endif diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 0285d8c8da..8d37e47acc 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -16,34 +16,31 @@ */ #include "MapManager.h" -#include "InstanceSaveMgr.h" +#include "Battleground.h" +#include "Containers.h" #include "DatabaseEnv.h" #include "DBCStores.h" -#include "Log.h" -#include "ObjectAccessor.h" -#include "Transport.h" -#include "GridDefines.h" -#include "MapInstanced.h" -#include "InstanceScript.h" -#include "Config.h" -#include "World.h" -#include "Corpse.h" -#include "ObjectMgr.h" -#include "Language.h" -#include "WorldPacket.h" #include "Group.h" -#include "Player.h" -#include "WorldSession.h" +#include "InstanceSaveMgr.h" +#include "Language.h" +#include "Log.h" +#include "Map.h" #include "Opcodes.h" +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include +#include MapManager::MapManager() - : _nextInstanceId(0), _scheduledScripts(0) + : _freeInstanceIds(std::make_unique()), _nextInstanceId(0), _scheduledScripts(0) { i_gridCleanUpDelay = sWorld->getIntConfig(CONFIG_INTERVAL_GRIDCLEAN); i_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE)); } -MapManager::~MapManager() { } +MapManager::~MapManager() = default; void MapManager::Initialize() { @@ -67,79 +64,182 @@ MapManager* MapManager::instance() return &instance; } -Map* MapManager::CreateBaseMap(uint32 id) +Map* MapManager::FindMap_i(uint32 mapId, uint32 instanceId) const { - Map* map = FindBaseMap(id); + return Trinity::Containers::MapGetValuePtr(i_maps, { mapId, instanceId }); +} - if (!map) +Map* MapManager::CreateWorldMap(uint32 mapId, uint32 instanceId) +{ + Map* map = new Map(mapId, i_gridCleanUpDelay, instanceId, REGULAR_DIFFICULTY); + map->LoadRespawnTimes(); + map->LoadCorpseData(); + + if (sWorld->getBoolConfig(CONFIG_BASEMAP_LOAD_GRIDS)) + map->LoadAllCells(); + + return map; +} + +InstanceMap* MapManager::CreateInstance(uint32 mapId, uint32 instanceId, InstanceSave* save, Difficulty difficulty, TeamId team) +{ + // make sure we have a valid map id + MapEntry const* entry = sMapStore.LookupEntry(mapId); + if (!entry) { - MapEntry const* entry = sMapStore.AssertEntry(id); - if (entry->ParentMapID != -1) + TC_LOG_ERROR("maps", "CreateInstance: no entry for map %d", mapId); + ABORT(); + } + + // some instances only have one difficulty + sDBCManager.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(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 (sWorld->getBoolConfig(CONFIG_INSTANCEMAP_LOAD_GRIDS)) + map->LoadAllCells(); + + return map; +} + +BattlegroundMap* MapManager::CreateBattleground(uint32 mapId, uint32 instanceId, Battleground* bg) +{ + TC_LOG_DEBUG("maps", "MapInstanced::CreateBattleground: map bg %d for %d created.", instanceId, mapId); + + BattlegroundMap* map = new BattlegroundMap(mapId, i_gridCleanUpDelay, instanceId, REGULAR_DIFFICULTY); + ASSERT(map->IsBattlegroundOrArena()); + map->SetBG(bg); + bg->SetBgMap(map); + return map; +} + +/* +- 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*/) +{ + if (!player) + return nullptr; + + MapEntry const* entry = sMapStore.LookupEntry(mapId); + if (!entry) + return nullptr; + + std::unique_lock lock(_mapsLock); + + Map* map = nullptr; + uint32 newInstanceId = 0; // instanceId of the resulting map + + if (entry->IsBattlegroundOrArena()) + { + // 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) { - CreateBaseMap(entry->ParentMapID); - - // must have been created by parent map - map = FindBaseMap(id); - return ASSERT_NOTNULL(map); + if (Battleground* bg = player->GetBattleground()) + map = CreateBattleground(mapId, newInstanceId, bg); + else + { + player->TeleportToBGEntryPoint(); + return nullptr; + } } - - std::lock_guard lock(_mapsLock); - map = CreateBaseMap_i(entry); } - - ASSERT(map); - return map; -} - -Map* MapManager::CreateBaseMap_i(MapEntry const* mapEntry) -{ - Map* map; - if (mapEntry->Instanceable()) - map = new MapInstanced(mapEntry->ID, i_gridCleanUpDelay); - else - map = new Map(mapEntry->ID, i_gridCleanUpDelay, 0, REGULAR_DIFFICULTY); - - i_maps[mapEntry->ID] = map; - - if (!mapEntry->Instanceable()) + else if (entry->IsDungeon()) { - map->LoadRespawnTimes(); - map->LoadCorpseData(); + InstancePlayerBind* pBind = player->GetBoundInstance(mapId, player->GetDifficulty(entry->IsRaid())); + 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->GetDifficulty(), 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->GetDifficulty(entry->IsRaid())); + 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->GetDifficulty(), player->GetTeamId()); + } + else + { + Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(entry->IsRaid()) : player->GetDifficulty(entry->IsRaid()); + + // if no instanceId via group members or instance saves is found + // the instance will be created for the first time + newInstanceId = GenerateInstanceId(); + + //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 + { + newInstanceId = 0; + map = FindMap_i(mapId, newInstanceId); + if (!map) + map = CreateWorldMap(mapId, newInstanceId); } + if (map) + i_maps[{ map->GetId(), map->GetInstanceId() }] = map; + return map; } -Map* MapManager::FindBaseNonInstanceMap(uint32 mapId) const +Map* MapManager::FindMap(uint32 mapId, uint32 instanceId) const { - Map* map = FindBaseMap(mapId); - if (map && map->Instanceable()) - return nullptr; - return map; -} - -Map* MapManager::CreateMap(uint32 id, Player* player, uint32 loginInstanceId) -{ - Map* m = CreateBaseMap(id); - - if (m && m->Instanceable()) - m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player, loginInstanceId); - - return m; -} - -Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const -{ - Map* map = FindBaseMap(mapid); - if (!map) - return nullptr; - - if (!map->Instanceable()) - return instanceId == 0 ? map : nullptr; - - return ((MapInstanced*)map)->FindInstanceMap(instanceId); + std::shared_lock lock(_mapsLock); + return FindMap_i(mapId, instanceId); } +/* Map::EnterState MapManager::PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck) { MapEntry const* entry = sMapStore.LookupEntry(mapid); @@ -229,7 +329,7 @@ Map::EnterState MapManager::PlayerCannotEnter(uint32 mapid, Player* player, bool else return Map::CANNOT_ENTER_UNSPECIFIED_REASON; } - +*/ void MapManager::Update(uint32 diff) { i_timer.Update(diff); @@ -237,12 +337,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(); @@ -253,7 +365,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, bool startUp) { @@ -285,37 +412,16 @@ void MapManager::UnloadAll() Map::DeleteStateMachine(); } -uint32 MapManager::GetNumInstances() +uint32 MapManager::GetNumInstances() const { - std::lock_guard 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 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 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 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() @@ -323,18 +429,18 @@ void MapManager::InitInstanceIds() _nextInstanceId = 1; if (QueryResult result = CharacterDatabase.Query("SELECT IFNULL(MAX(id), 0) FROM instance")) - _freeInstanceIds.resize((*result)[0].GetUInt64() + 2, true); // make space for one extra to be able to access [_nextInstanceId] index in case all slots are taken + _freeInstanceIds->resize((*result)[0].GetUInt64() + 2, true); // make space for one extra to be able to access [_nextInstanceId] index in case all slots are taken else - _freeInstanceIds.resize(_nextInstanceId + 1, true); + _freeInstanceIds->resize(_nextInstanceId + 1, true); // never allow 0 id - _freeInstanceIds[0] = false; + _freeInstanceIds->set(0, false); } void MapManager::RegisterInstanceId(uint32 instanceId) { // Allocation and sizing was done in InitInstanceIds() - _freeInstanceIds[instanceId] = false; + _freeInstanceIds->set(instanceId, false); // Instances are pulled in ascending order from db and nextInstanceId is initialized with 1, // so if the instance id is used, increment until we find the first unused one for a potential new instance @@ -352,15 +458,15 @@ uint32 MapManager::GenerateInstanceId() } uint32 newInstanceId = _nextInstanceId; - ASSERT(newInstanceId < _freeInstanceIds.size()); - _freeInstanceIds[newInstanceId] = false; + ASSERT(newInstanceId < _freeInstanceIds->size()); + _freeInstanceIds->set(newInstanceId, false); // Find the lowest available id starting from the current NextInstanceId (which should be the lowest according to the logic in FreeInstanceId() - size_t nextFreedId = _freeInstanceIds.find_next(_nextInstanceId++); + size_t nextFreedId = _freeInstanceIds->find_next(_nextInstanceId++); if (nextFreedId == InstanceIds::npos) { - _nextInstanceId = uint32(_freeInstanceIds.size()); - _freeInstanceIds.push_back(true); + _nextInstanceId = uint32(_freeInstanceIds->size()); + _freeInstanceIds->push_back(true); } else _nextInstanceId = uint32(nextFreedId); @@ -372,5 +478,5 @@ void MapManager::FreeInstanceId(uint32 instanceId) { // If freed instance id is lower than the next id available for new instances, use the freed one instead _nextInstanceId = std::min(instanceId, _nextInstanceId); - _freeInstanceIds[instanceId] = true; + _freeInstanceIds->set(instanceId, true); } diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 9c9be1b58f..22158cb2fb 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -18,29 +18,41 @@ #ifndef TRINITY_MAPMANAGER_H #define TRINITY_MAPMANAGER_H -#include "Object.h" -#include "Map.h" -#include "MapInstanced.h" -#include "GridStates.h" +#include "GridDefines.h" +#include "IteratorPair.h" #include "MapUpdater.h" -#include +#include "Position.h" +#include "SharedDefines.h" +#include +#include +#include -class PhaseShift; -class Transport; -struct TransportCreatureProto; +class Battleground; +class BattlegroundMap; +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) { @@ -59,7 +71,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, bool startUp); @@ -89,14 +100,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(); @@ -118,29 +126,24 @@ class TC_GAME_API MapManager bool IsScriptScheduled() const { return _scheduledScripts > 0; } private: - typedef std::unordered_map MapMapType; + using MapKey = std::pair; + typedef std::map MapMapType; typedef boost::dynamic_bitset 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); - 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; - InstanceIds _freeInstanceIds; + std::unique_ptr _freeInstanceIds; uint32 _nextInstanceId; MapUpdater m_updater; @@ -151,40 +154,24 @@ class TC_GAME_API MapManager template inline void MapManager::DoForAllMaps(Worker&& worker) { - std::lock_guard lock(_mapsLock); + std::shared_lock 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 -inline void MapManager::DoForAllMapsWithMapId(uint32 mapId, Worker&& worker) +void MapManager::DoForAllMapsWithMapId(uint32 mapId, Worker&& worker) { - std::lock_guard lock(_mapsLock); + std::shared_lock 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& instancePair : instances) - worker(instancePair.second); - } - else - worker(map); - } + auto range = Trinity::Containers::MakeIteratorPair( + i_maps.lower_bound({ mapId, 0 }), + i_maps.upper_bound({ mapId, std::numeric_limits::max() }) + ); + + for (auto const& [key, map] : range) + worker(map); } #define sMapMgr MapManager::instance() diff --git a/src/server/game/Maps/TerrainMgr.cpp b/src/server/game/Maps/TerrainMgr.cpp index c3c5308bce..2db23ee80b 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 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 childTerrain) @@ -170,6 +174,8 @@ void TerrainInfo::LoadMapAndVMapImpl(int32 gx, int32 gy) for (std::shared_ptr 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()); @@ -245,6 +251,8 @@ void TerrainInfo::UnloadMapImpl(int32 gx, int32 gy) for (std::shared_ptr const& childTerrain : _childTerrain) childTerrain->UnloadMapImpl(gx, gy); + + _loadedGrids[GetBitsetIndex(gx, gy)] = false; } GridMap* TerrainInfo::GetGrid(uint32 mapId, float x, float y, bool loadIfMissing /*= true*/) @@ -254,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 lock(_loadMutex); LoadMapAndVMapImpl(gx, gy); @@ -283,7 +291,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.count()); diff --git a/src/server/game/Maps/TerrainMgr.h b/src/server/game/Maps/TerrainMgr.h index 43b67c613a..0e163eaf30 100644 --- a/src/server/game/Maps/TerrainMgr.h +++ b/src/server/game/Maps/TerrainMgr.h @@ -98,6 +98,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; @@ -106,6 +108,7 @@ private: std::mutex _loadMutex; std::unique_ptr _gridMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; std::atomic _referenceCountFromMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; + std::bitset _loadedGrids; std::bitset _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 4eb40d02b1..b128d87043 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -22,7 +22,7 @@ #include "GameObjectData.h" #include "InstanceScript.h" #include "Log.h" -#include "MapManager.h" +#include "Map.h" #include "MoveSplineInitArgs.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" diff --git a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp index 9ef20b48d8..b6dc776d8e 100644 --- a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp @@ -19,7 +19,7 @@ #include "DBCStores.h" #include "GameEventSender.h" #include "Log.h" -#include "MapManager.h" +#include "Map.h" #include "MoveSpline.h" #include "MoveSplineInit.h" #include "ObjectMgr.h" @@ -190,7 +190,7 @@ bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/) break; if (_currentNode == _preloadTargetNode) - PreloadEndGrid(); + PreloadEndGrid(player); _currentNode += departureEvent ? 1 : 0; departureEvent = !departureEvent; } while (_currentNode < _path.size() - 1); @@ -240,14 +240,18 @@ void FlightPathMovementGenerator::InitEndGridInfo() uint32 nodeCount = _path.size(); //! Number of nodes in path. _endMapId = _path[nodeCount - 1]->ContinentID; //! MapId of last node _preloadTargetNode = nodeCount - 3; + + while (_path[_preloadTargetNode]->ContinentID != _endMapId) + ++_preloadTargetNode; + _endGridX = _path[nodeCount - 1]->Loc.X; _endGridY = _path[nodeCount - 1]->Loc.Y; } -void FlightPathMovementGenerator::PreloadEndGrid() +void FlightPathMovementGenerator::PreloadEndGrid(Player* owner) { - // used to preload the final grid where the flightmaster is - Map* endMap = sMapMgr->FindBaseNonInstanceMap(_endMapId); + // Used to preload the final grid where the flightmaster is + Map* endMap = owner->GetMap(); // Load the grid if (endMap) diff --git a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h index abc42355a6..e378a2bbc3 100644 --- a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h @@ -49,7 +49,7 @@ public: bool GetResetPos(Player*, float& x, float& y, float& z); void InitEndGridInfo(); - void PreloadEndGrid(); + void PreloadEndGrid(Player* owner); private: diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index 2b4a4f8f6c..033cd48e24 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -18,7 +18,6 @@ #include "WaypointManager.h" #include "DatabaseEnv.h" #include "GridDefines.h" -#include "MapManager.h" #include "Log.h" void WaypointMgr::Load() diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index 350d81d819..3e13b7de31 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -22,7 +22,6 @@ #include "Group.h" #include "Log.h" #include "Map.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 174aa7379d..94baaea4fb 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -27,6 +27,7 @@ #include "Item.h" #include "LFGScripts.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index abb195da2a..b7cfa8983a 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -35,6 +35,7 @@ #include "GuildMgr.h" #include "IpAddress.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "Metric.h" #include "ObjectAccessor.h" diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 7f3eef7769..5134a89e14 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -727,7 +727,7 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex effIndex) continue; } - Map::EnterState enterState = sMapMgr->PlayerCannotEnter(player->GetMapId(), member, false); + Map::EnterState enterState = Map::PlayerCannotEnter(player->GetMapId(), member, false); if (enterState != Map::CAN_ENTER) { if (enterState == Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index cef66d6663..1abe3715da 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -59,6 +59,7 @@ #include "LootItemStorage.h" #include "LootMgr.h" #include "M2Stores.h" +#include "Map.h" #include "MapManager.h" #include "Metric.h" #include "MMapFactory.h" @@ -2346,19 +2347,6 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Calculate next currency reset time..."); InitCurrencyResetTime(); - // Preload all cells, if required for the base maps - if (sWorld->getBoolConfig(CONFIG_BASEMAP_LOAD_GRIDS)) - { - sMapMgr->DoForAllMaps([](Map* map) - { - if (!map->Instanceable()) - { - TC_LOG_INFO("server.loading", "Pre-loading base map data for map %u", map->GetId()); - map->LoadAllCells(); - } - }); - } - uint32 startupDuration = GetMSTimeDiffToNow(startupBegin); TC_LOG_INFO("server.worldserver", "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000)); diff --git a/src/server/scripts/Battlefield/BattlefieldTB.cpp b/src/server/scripts/Battlefield/BattlefieldTB.cpp index a6987d9805..93b78feeb9 100644 --- a/src/server/scripts/Battlefield/BattlefieldTB.cpp +++ b/src/server/scripts/Battlefield/BattlefieldTB.cpp @@ -28,7 +28,6 @@ #include "Creature.h" #include "GameObject.h" #include "GameTime.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Player.h" #include "Random.h" diff --git a/src/server/scripts/Battlefield/BattlefieldWG.cpp b/src/server/scripts/Battlefield/BattlefieldWG.cpp index ce75b550d0..5f8bd25598 100644 --- a/src/server/scripts/Battlefield/BattlefieldWG.cpp +++ b/src/server/scripts/Battlefield/BattlefieldWG.cpp @@ -27,7 +27,6 @@ #include "GameObject.h" #include "GameTime.h" #include "Log.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 534af49c35..4455b4e9a8 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "DBCStores.h" #include "GossipDef.h" #include "GridNotifiersImpl.h" +#include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Language.h" #include "Log.h" @@ -1622,14 +1623,31 @@ public: if (*args) { int32 mapId = atoi(args); - map = sMapMgr->FindBaseNonInstanceMap(mapId); + sMapMgr->DoForAllMapsWithMapId(mapId, [&](Map* map) + { + HandleDebugLoadCellsCommandHelper(handler, map); + }); + return true; } + if (!map) - map = player->GetMap(); + { + // Fallback to player's map if no map has been specified + return HandleDebugLoadCellsCommandHelper(handler, player->GetMap()); + } + + return false; + } + + static bool HandleDebugLoadCellsCommandHelper(ChatHandler* handler, Map* map) + { + if (!map) + return false; handler->PSendSysMessage("Loading all cells (mapId: %u). Current next GameObject %u, Creature %u", map->GetId(), map->GetMaxLowGuid(), map->GetMaxLowGuid()); map->LoadAllCells(); handler->PSendSysMessage("Cells loaded (mapId: %u) After load - Next GameObject %u, Creature %u", map->GetId(), map->GetMaxLowGuid(), map->GetMaxLowGuid()); + return true; } diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 497ecc1672..70dfb3aad7 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "GameObject.h" #include "Language.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 54e75814a7..f05b1bf6ff 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -29,6 +29,7 @@ EndScriptData */ #include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Language.h" +#include "Map.h" #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index a287a5bbde..6fc49f2ce1 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -32,6 +32,7 @@ EndScriptData */ #include "GameTime.h" #include "Language.h" #include "Map.h" +#include "Map.h" #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -129,28 +130,18 @@ public: bool liveFound = false; // Get map (only support base map from console) - Map* thisMap; + Map* thisMap = nullptr; if (handler->GetSession()) thisMap = handler->GetSession()->GetPlayer()->GetMap(); - else - thisMap = sMapMgr->FindBaseNonInstanceMap(mapId); // If map found, try to find active version of this creature if (thisMap) { - auto const creBounds = thisMap->GetCreatureBySpawnIdStore().equal_range(guid); - if (creBounds.first != creBounds.second) - { - for (std::unordered_multimap::const_iterator itr = creBounds.first; itr != creBounds.second;) - { - if (handler->GetSession()) - handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " "); - else - handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " "); - ++itr; - } - liveFound = true; - } + auto const creBounds = Trinity::Containers::MapEqualRange(thisMap->GetCreatureBySpawnIdStore(), guid); + for (auto& [spawnId, creature] : creBounds) + handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, std::to_string(guid).c_str(), std::to_string(guid).c_str(), cInfo->Name.c_str(), + x, y, z, mapId, creature->GetGUID().ToString().c_str(), creature->IsAlive() ? "*" : " "); + liveFound = creBounds.begin() != creBounds.end(); } if (!liveFound) @@ -448,28 +439,18 @@ public: bool liveFound = false; // Get map (only support base map from console) - Map* thisMap; + Map* thisMap = nullptr; if (handler->GetSession()) thisMap = handler->GetSession()->GetPlayer()->GetMap(); - else - thisMap = sMapMgr->FindBaseNonInstanceMap(mapId); // If map found, try to find active version of this object if (thisMap) { - auto const goBounds = thisMap->GetGameObjectBySpawnIdStore().equal_range(guid); - if (goBounds.first != goBounds.second) - { - for (std::unordered_multimap::const_iterator itr = goBounds.first; itr != goBounds.second;) - { - if (handler->GetSession()) - handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " "); - else - handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " "); - ++itr; - } - liveFound = true; - } + auto const goBounds = Trinity::Containers::MapEqualRange(thisMap->GetGameObjectBySpawnIdStore(), guid); + for (auto& [spawnId, go] : goBounds) + handler->PSendSysMessage(LANG_GO_LIST_CHAT, std::to_string(guid).c_str(), entry, std::to_string(guid).c_str(), gInfo->name.c_str(), x, y, z, mapId, + go->GetGUID().ToString().c_str(), go->isSpawned() ? "*" : " "); + liveFound = goBounds.begin() != goBounds.end(); } if (!liveFound) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_maloriak.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_maloriak.cpp index 70488c8d4d..561087a5b2 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_maloriak.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_maloriak.cpp @@ -16,20 +16,19 @@ */ #include "ObjectMgr.h" -#include "ScriptMgr.h" +#include "blackwing_descent.h" #include "CommonPredicates.h" #include "DBCStores.h" #include "GridNotifiers.h" -#include "PassiveAI.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellAuraEffects.h" -#include "SpellMgr.h" #include "InstanceScript.h" #include "MotionMaster.h" +#include "PassiveAI.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" +#include "SpellAuraEffects.h" +#include "SpellMgr.h" +#include "SpellScript.h" #include "TemporarySummon.h" -#include "MapInstanced.h" -#include "blackwing_descent.h" enum Spells { diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp index 270e0af5fb..74c1778de6 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp @@ -17,7 +17,7 @@ #include "ScriptMgr.h" #include "GameObject.h" -#include "MapManager.h" +#include "Map.h" #include "OutdoorPvP.h" #include "OutdoorPvPHP.h" #include "OutdoorPvPMgr.h" @@ -260,7 +260,7 @@ void OPvPCapturePointHP::ChangeState() break; } - Map* map = sMapMgr->FindMap(530, 0); + Map* map = m_PvP->GetMap(); auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(m_capturePointSpawnId); for (auto itr = bounds.first; itr != bounds.second; ++itr) itr->second->SetGoArtKit(artkit);