diff options
author | Shauren <shauren.trinity@gmail.com> | 2022-07-24 18:56:40 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-07-24 18:56:40 +0200 |
commit | fbe0b8efebca3bc2662b477bdf45627f9783d6c2 (patch) | |
tree | 6f5662045be0c4e5f06d8a97f9669b7f93cf2df1 /src | |
parent | 8f2c5413d7a3775a304c793653d41caabf3ba997 (diff) |
Core/Maps: Removed MapInstanced - no longer neccessary for grid data reference counting (moved to TerrainInfo)
Diffstat (limited to 'src')
34 files changed, 496 insertions, 805 deletions
diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index 02be080a4a7..4a81ad485d3 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -34,6 +34,7 @@ #include "Item.h" #include "LanguageMgr.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "ObjectMgr.h" #include "PhasingHandler.h" diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 94881ea0f32..9fc08c82604 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -28,11 +28,9 @@ #include "GroupMgr.h" #include "Log.h" #include "Map.h" -#include "MapManager.h" #include "MiscPackets.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" -#include "WorldSession.h" #include <G3D/g3dmath.h> Battlefield::Battlefield(Map* map) diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 29fcc2c7719..58e8243b990 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -37,8 +37,6 @@ #include "GameEventMgr.h" #include "Language.h" #include "Log.h" -#include "Map.h" -#include "MapInstanced.h" #include "MapManager.h" #include "ObjectMgr.h" #include "Player.h" diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 4bc63a2c304..f2d7be4ae62 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1430,7 +1430,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; // Seamless teleport can happen only if cosmetic maps match diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index cd6d33e5f7b..eb8e5aea43a 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 daf0a510deb..b18b36aeedc 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/Garrison/GarrisonMap.cpp b/src/server/game/Garrison/GarrisonMap.cpp index 33058bac1b6..4bafb964dab 100644 --- a/src/server/game/Garrison/GarrisonMap.cpp +++ b/src/server/game/Garrison/GarrisonMap.cpp @@ -100,8 +100,8 @@ void GarrisonGridLoader::Visit(CreatureMapType& /*m*/) } -GarrisonMap::GarrisonMap(uint32 id, time_t expiry, uint32 instanceId, Map* parent, ObjectGuid const& owner) - : Map(id, expiry, instanceId, DIFFICULTY_NORMAL, parent), _owner(owner), _loadingPlayer(nullptr) +GarrisonMap::GarrisonMap(uint32 id, time_t expiry, uint32 instanceId, ObjectGuid const& owner) + : Map(id, expiry, instanceId, DIFFICULTY_NORMAL), _owner(owner), _loadingPlayer(nullptr) { GarrisonMap::InitVisibilityDistance(); } diff --git a/src/server/game/Garrison/GarrisonMap.h b/src/server/game/Garrison/GarrisonMap.h index dd9c41baf57..4fbcb240e39 100644 --- a/src/server/game/Garrison/GarrisonMap.h +++ b/src/server/game/Garrison/GarrisonMap.h @@ -26,7 +26,7 @@ class Player; class TC_GAME_API GarrisonMap : public Map { public: - GarrisonMap(uint32 id, time_t, uint32 instanceId, Map* parent, ObjectGuid const& owner); + GarrisonMap(uint32 id, time_t, uint32 instanceId, ObjectGuid const& owner); void LoadGridObjects(NGridType* grid, Cell const& cell) override; Garrison* GetGarrison(); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index aff58ca046d..b4b2ca8a98b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -36,9 +36,11 @@ #include "Guild.h" #include "GuildMgr.h" #include "InstancePackets.h" +#include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Language.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "MiscPackets.h" #include "Object.h" @@ -571,7 +573,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge 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 7e2d1a98129..0c027ea5f33 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -25,6 +25,7 @@ #include "InstancePackets.h" #include "InstanceSaveMgr.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "MiscPackets.h" #include "MovementPackets.h" diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index 6e40a6fc026..b566b4f0cf6 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -28,7 +28,6 @@ #include "InstanceScript.h" #include "Log.h" #include "Map.h" -#include "MapInstanced.h" #include "MapManager.h" #include "ObjectMgr.h" #include "Player.h" @@ -607,7 +606,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; @@ -617,7 +616,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) { ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY); @@ -696,28 +695,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 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" diff --git a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp index a58b6b7f824..b34809b2f51 100644 --- a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.cpp @@ -19,7 +19,7 @@ #include "DB2Stores.h" #include "GameEventSender.h" #include "Log.h" -#include "MapManager.h" +#include "Map.h" #include "MovementDefines.h" #include "MoveSpline.h" #include "MoveSplineInit.h" @@ -132,7 +132,7 @@ bool FlightPathMovementGenerator::DoUpdate(Player* owner, uint32 /*diff*/) break; if (_currentNode == _preloadTargetNode) - PreloadEndGrid(); + PreloadEndGrid(owner); _currentNode += departureEvent ? 1 : 0; departureEvent = !departureEvent; @@ -284,14 +284,18 @@ void FlightPathMovementGenerator::InitEndGridInfo() _preloadTargetNode = 0; else _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); + 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 7252b1effe1..10bbab589e2 100644 --- a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h @@ -54,7 +54,7 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, Fligh void SkipCurrentNode() { ++_currentNode; } void DoEventIfAny(Player* owner, TaxiPathNodeEntry const* node, bool departure); void InitEndGridInfo(); - void PreloadEndGrid(); + void PreloadEndGrid(Player* owner); std::string GetDebugInfo() const override; diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index a7c76ad3c90..182f7c35065 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 97ac8a83d4e..1e609b15272 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -23,7 +23,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/World/World.cpp b/src/server/game/World/World.cpp index 0d2fd838d75..5b741bda0ca 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -67,6 +67,7 @@ #include "LootItemStorage.h" #include "LootMgr.h" #include "M2Stores.h" +#include "Map.h" #include "MapManager.h" #include "Metric.h" #include "MiscPackets.h" @@ -2493,19 +2494,6 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading phase names..."); sObjectMgr->LoadPhaseNames(); - // 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 93ed4da8131..c49d92eb6bc 100644 --- a/src/server/scripts/Battlefield/BattlefieldTB.cpp +++ b/src/server/scripts/Battlefield/BattlefieldTB.cpp @@ -29,7 +29,6 @@ #include "CreatureTextMgr.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 895868b9cec..07400151e52 100644 --- a/src/server/scripts/Battlefield/BattlefieldWG.cpp +++ b/src/server/scripts/Battlefield/BattlefieldWG.cpp @@ -28,7 +28,6 @@ #include "GameObject.h" #include "GameTime.h" #include "Log.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "Player.h" #include "Random.h" diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index b3879bafd56..72e3ffc6838 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -37,6 +37,7 @@ EndScriptData */ #include "DB2Stores.h" #include "GameTime.h" #include "GridNotifiersImpl.h" +#include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Language.h" #include "Log.h" @@ -1233,17 +1234,26 @@ public: static bool HandleDebugLoadCellsCommand(ChatHandler* handler, Optional<uint32> mapId, Optional<uint32> tileX, Optional<uint32> tileY) { - Map* map = nullptr; if (mapId) { - map = sMapMgr->FindBaseNonInstanceMap(*mapId); + sMapMgr->DoForAllMapsWithMapId(*mapId, [&](Map* map) + { + HandleDebugLoadCellsCommandHelper(handler, map, tileX, tileY); + }); + return true; } - else if (Player* player = handler->GetPlayer()) + + if (Player* player = handler->GetPlayer()) { // Fallback to player's map if no map has been specified - map = player->GetMap(); + return HandleDebugLoadCellsCommandHelper(handler, player->GetMap(), tileX, tileY); } + return false; + } + + static bool HandleDebugLoadCellsCommandHelper(ChatHandler* handler, Map* map, Optional<uint32> tileX, Optional<uint32> tileY) + { if (!map) return false; diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index e389f535569..027c72e1c23 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -33,6 +33,7 @@ EndScriptData */ #include "GameTime.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 eb5173b1daf..a00853298c0 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "InstanceSaveMgr.h" #include "InstanceScript.h" #include "Language.h" +#include "Map.h" #include "MapManager.h" #include "Player.h" #include "RBAC.h" diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 61668b98973..5ca3df7caca 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -32,7 +32,7 @@ EndScriptData */ #include "GameObject.h" #include "GameTime.h" #include "Language.h" -#include "MapManager.h" +#include "Map.h" #include "ObjectMgr.h" #include "PhasingHandler.h" #include "Player.h" @@ -120,28 +120,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<ObjectGuid::LowType, Creature*>::const_iterator itr = creBounds.first; itr != creBounds.second;) - { - if (handler->GetSession()) - 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, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " "); - else - handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, std::to_string(guid).c_str(), 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) @@ -401,28 +391,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<ObjectGuid::LowType, GameObject*>::const_iterator itr = goBounds.first; itr != goBounds.second;) - { - if (handler->GetSession()) - 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, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " "); - else - handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, std::to_string(guid).c_str(), 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/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 763fa92044f..850f9fd886a 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -20,7 +20,7 @@ #include "GameObject.h" #include "GameObjectAI.h" #include "InstanceScript.h" -#include "MapManager.h" +#include "Map.h" #include "MotionMaster.h" #include "MoveSplineInit.h" #include "PassiveAI.h" diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp index 1cca54645c5..1a880c0748e 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPHP.cpp @@ -17,7 +17,7 @@ #include "OutdoorPvPHP.h" #include "GameObject.h" -#include "MapManager.h" +#include "Map.h" #include "Player.h" #include "ScriptMgr.h" #include "WorldStatePackets.h" diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp index 6534c86bfeb..20345358fe5 100644 --- a/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp +++ b/src/server/scripts/OutdoorPvP/OutdoorPvPZM.cpp @@ -19,7 +19,6 @@ #include "Creature.h" #include "GameObject.h" #include "GossipDef.h" -#include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" |