diff options
author | Treeston <treeston.mmoc@gmail.com> | 2015-12-31 19:51:07 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-03-21 19:41:28 +0100 |
commit | 7422557d03b3cbb5b00a39b16c52f879c2f31fdd (patch) | |
tree | c379a50a7ba112b8a4f5149f6c89973939a8675d | |
parent | 96ca739a0b0d2b34a28fbac0593e076fb3c42f1b (diff) |
Merge pull request #16110 from Treeston/3.3.5-instancerevive
Game/Maps: Clean up instance zone-in handling
(cherry picked from commit eb3dc8a4f064d89b78248a511bcf67f805ea3314)
-rw-r--r-- | sql/updates/world/2016_03_21_01_world_2015_12_27_instancerevive.sql | 4 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 101 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 63 | ||||
-rw-r--r-- | src/server/game/Handlers/MovementHandler.cpp | 19 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceScript.h | 2 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 60 | ||||
-rw-r--r-- | src/server/game/Maps/Map.h | 22 | ||||
-rw-r--r-- | src/server/game/Maps/MapInstanced.cpp | 31 | ||||
-rw-r--r-- | src/server/game/Maps/MapInstanced.h | 6 | ||||
-rw-r--r-- | src/server/game/Maps/MapManager.cpp | 64 | ||||
-rw-r--r-- | src/server/game/Maps/MapManager.h | 4 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/Language.h | 4 |
13 files changed, 241 insertions, 141 deletions
diff --git a/sql/updates/world/2016_03_21_01_world_2015_12_27_instancerevive.sql b/sql/updates/world/2016_03_21_01_world_2015_12_27_instancerevive.sql new file mode 100644 index 00000000000..3bee62963ef --- /dev/null +++ b/sql/updates/world/2016_03_21_01_world_2015_12_27_instancerevive.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `trinity_string` WHERE `entry`=11014; +INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES +(11014,"You are already locked to %s."); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a9b7aa12b6b..f30b4ac5c44 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1558,7 +1558,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->CanPlayerEnter(mapid, this, false)) + if (sMapMgr->PlayerCannotEnter(mapid, this, false)) return false; // Seamless teleport can happen only if cosmetic maps match @@ -16634,7 +16634,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) // NOW player must have valid map // load the player's map here if it's not already loaded - Map* map = sMapMgr->CreateMap(mapId, this); + Map* map = sMapMgr->CreateMap(mapId, this, instanceId); AreaTriggerStruct const* areaTrigger = nullptr; bool check = false; @@ -16645,8 +16645,28 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) } else if (map->IsDungeon()) // if map is dungeon... { - if (!((InstanceMap*)map)->CanEnter(this) || !CheckInstanceLoginValid(map)) // ... and can't enter map, then look for entry point. + if (Map::EnterState denyReason = ((InstanceMap*)map)->CannotEnter(this)) // ... and can't enter map, then look for entry point. { + switch (denyReason) + { + case Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE: + SendTransferAborted(map->GetId(), TRANSFER_ABORT_DIFFICULTY, map->GetDifficultyID()); + break; + case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH: + ChatHandler(GetSession()).PSendSysMessage(GetSession()->GetTrinityString(LANG_INSTANCE_BIND_MISMATCH), map->GetMapName()); + break; + case Map::CANNOT_ENTER_TOO_MANY_INSTANCES: + SendTransferAborted(map->GetId(), TRANSFER_ABORT_TOO_MANY_INSTANCES); + break; + case Map::CANNOT_ENTER_MAX_PLAYERS: + SendTransferAborted(map->GetId(), TRANSFER_ABORT_MAX_PLAYERS); + break; + case Map::CANNOT_ENTER_ZONE_IN_COMBAT: + SendTransferAborted(map->GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); + break; + default: + break; + } areaTrigger = sObjectMgr->GetGoBackTrigger(mapId); check = true; } @@ -16692,6 +16712,10 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetMap(map); + // now that map position is determined, check instance validity + if (!CheckInstanceValidity(true) && !IsInstanceLoginGameMasterException()) + m_InstanceValid = false; + // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE] // this must help in case next save after mass player load after server startup m_nextSave = urand(m_nextSave / 2, m_nextSave * 3 / 2); @@ -18387,37 +18411,68 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report return true; } -bool Player::CheckInstanceLoginValid(Map* map) +bool Player::IsInstanceLoginGameMasterException() const { - if (!map->IsDungeon() || IsInstanceLoginGameMasterException()) + if (CanBeGameMaster()) + { + ChatHandler(GetSession()).SendSysMessage(LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION); + return true; + } + else + return false; +} + +bool Player::CheckInstanceValidity(bool /*isLogin*/) +{ + // game masters' instances are always valid + if (IsGameMaster()) + return true; + + // non-instances are always valid + Map* map = GetMap(); + if (!map || !map->IsDungeon()) return true; - if (map->IsRaid()) + // raid instances require the player to be in a raid group to be valid + if (map->IsRaid() && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID)) + if (!GetGroup() || !GetGroup()->isRaidGroup()) + return false; + + if (Group* group = GetGroup()) { - // cannot be in raid instance without a group - if (!GetGroup()) + // check if player's group is bound to this instance + InstanceGroupBind* bind = group->GetBoundInstance(map->GetDifficultyID(), map->GetId()); + if (!bind || !bind->save || bind->save->GetInstanceId() != map->GetInstanceId()) return false; + + Map::PlayerList const& players = map->GetPlayers(); + if (!players.isEmpty()) + for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + { + if (Player* otherPlayer = it->GetSource()) + { + if (otherPlayer->IsGameMaster()) + continue; + if (!otherPlayer->m_InstanceValid) // ignore players that currently have a homebind timer active + continue; + if (group != otherPlayer->GetGroup()) + return false; + } + } } else { - // cannot be in normal instance without a group and more players than 1 in instance - if (!GetGroup() && map->GetPlayersCountExceptGMs() > 1) + // instance is invalid if we are not grouped and there are other players + if (map->GetPlayersCountExceptGMs() > 1) return false; - } - - // do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player - return sMapMgr->CanPlayerEnter(map->GetId(), this, true); -} -bool Player::IsInstanceLoginGameMasterException() const -{ - if (CanBeGameMaster()) - { - ChatHandler(GetSession()).SendSysMessage(LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION); - return true; + // check if the player is bound to this instance + InstancePlayerBind* bind = GetBoundInstance(map->GetId(), map->GetDifficultyID()); + if (!bind || !bind->save || bind->save->GetInstanceId() != map->GetInstanceId()) + return false; } - else - return false; + + return true; } bool Player::CheckInstanceCount(uint32 instanceId) const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 6dccc24290d..0679fc9167f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2355,7 +2355,7 @@ class Player : public Unit, public GridObject<Player> void SendRaidInfo(); static void ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader); bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false); - bool CheckInstanceLoginValid(Map* map); + bool CheckInstanceValidity(bool /*isLogin*/); bool CheckInstanceCount(uint32 instanceId) const; void AddInstanceEnterTime(uint32 instanceId, time_t enterTime); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index b94b4ce838e..a9db9d52572 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -534,8 +534,69 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& pack bool teleported = false; if (player->GetMapId() != at->target_mapId) { - if (!sMapMgr->CanPlayerEnter(at->target_mapId, player, false)) + if (Map::EnterState denyReason = sMapMgr->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) + { + case Map::CANNOT_ENTER_NO_ENTRY: + TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter map with id %d which has no entry", player->GetName().c_str(), at->target_mapId); + break; + case Map::CANNOT_ENTER_UNINSTANCED_DUNGEON: + TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter dungeon map %d but no instance template was found", player->GetName().c_str(), at->target_mapId); + break; + case Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE: + TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter instance map %d but the requested difficulty was not found", player->GetName().c_str(), at->target_mapId); + if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId)) + player->SendTransferAborted(entry->ID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficultyID(entry)); + break; + case Map::CANNOT_ENTER_NOT_IN_RAID: + TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter map %d", player->GetName().c_str(), at->target_mapId); + player->SendRaidGroupOnlyMessage(RAID_GROUP_ERR_ONLY, 0); + reviveAtTrigger = true; + break; + case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE: + player->GetSession()->SendPacket(WorldPackets::Misc::AreaTriggerNoCorpse().Write()); + TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance map %d and cannot enter", player->GetName().c_str(), at->target_mapId); + break; + case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH: + if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId)) + { + char const* mapName = entry->MapName_lang; + TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map '%s' because their permanent bind is incompatible with their group's", player->GetName().c_str(), mapName); + // is there a special opcode for this? + // @todo figure out how to get player localized difficulty string (e.g. "10 player", "Heroic" etc) + ChatHandler(player->GetSession()).PSendSysMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_BIND_MISMATCH), mapName); + } + reviveAtTrigger = true; + break; + case Map::CANNOT_ENTER_TOO_MANY_INSTANCES: + player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_TOO_MANY_INSTANCES); + TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because he has exceeded the maximum number of instances per hour.", player->GetName().c_str(), at->target_mapId); + reviveAtTrigger = true; + break; + case Map::CANNOT_ENTER_MAX_PLAYERS: + player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAX_PLAYERS); + reviveAtTrigger = true; + break; + case Map::CANNOT_ENTER_ZONE_IN_COMBAT: + player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT); + reviveAtTrigger = true; + break; + default: + break; + } + + if (reviveAtTrigger) // check if the player is touching the areatrigger leading to the map his corpse is on + if (!player->IsAlive() && player->HasCorpse()) + if (player->GetCorpseLocation().GetMapId() == at->target_mapId) + { + player->ResurrectPlayer(0.5f); + player->SpawnCorpseBones(); + } + return; + } if (Group* group = player->GetGroup()) if (group->isLFGGroup() && player->GetMap()->IsDungeon()) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 3ab9f683dea..697060078bc 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -77,9 +77,9 @@ void WorldSession::HandleMoveWorldportAckOpcode() } // relocate the player to the teleport destination - // the CanEnter checks are done in TeleporTo but conditions may change + // the CannotEnter checks are done in TeleporTo but conditions may change // while the player is in transit, for example the map may get full - if (!newMap || !newMap->CanEnter(GetPlayer())) + if (!newMap || newMap->CannotEnter(GetPlayer())) { TC_LOG_ERROR("network", "Map %d (%s) could not be created for %s (%s), porting player to homebind", loc.GetMapId(), newMap ? newMap->GetMapName() : "Unknown", GetPlayer()->GetGUID().ToString().c_str(), GetPlayer()->GetName().c_str()); GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); @@ -157,19 +157,18 @@ void WorldSession::HandleMoveWorldportAckOpcode() } // resurrect character at enter into instance where his corpse exist after add to map - Corpse* corpse = GetPlayer()->GetMap()->GetCorpseByPlayer(GetPlayer()->GetGUID()); - if (corpse && corpse->GetType() != CORPSE_BONES) - { - if (mEntry->IsDungeon()) + + if (mEntry->IsDungeon() && !GetPlayer()->IsAlive()) + if (GetPlayer()->GetCorpseLocation().GetMapId() == mEntry->ID) { GetPlayer()->ResurrectPlayer(0.5f, false); GetPlayer()->SpawnCorpseBones(); } - } bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena(); if (mInstance) { + // check if this instance has a reset time and send it to player if so Difficulty diff = GetPlayer()->GetDifficultyID(mEntry); if (MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mEntry->ID, diff)) { @@ -182,6 +181,12 @@ void WorldSession::HandleMoveWorldportAckOpcode() } } } + + // check if instance is valid + if (!GetPlayer()->CheckInstanceValidity(false)) + GetPlayer()->m_InstanceValid = false; + + // instance mounting is handled in InstanceTemplate allowMount = mInstance->AllowMount; } diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index 56e97217e2a..02c9bfa408e 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -161,7 +161,7 @@ class InstanceScript : public ZoneScript virtual void Update(uint32 /*diff*/) { } - // Used by the map's CanEnter function. + // Used by the map's CannotEnter function. // This is to prevent players from entering during boss encounters. virtual bool IsEncounterInProgress() const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 90c02b10477..3dbed19fc98 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3062,62 +3062,38 @@ void InstanceMap::InitVisibilityDistance() /* Do map specific checks to see if the player can enter */ -bool InstanceMap::CanEnter(Player* player) +Map::EnterState InstanceMap::CannotEnter(Player* player) { if (player->GetMapRef().getTarget() == this) { - TC_LOG_ERROR("maps", "InstanceMap::CanEnter - player %s(%s) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetId(), GetInstanceId(), GetSpawnMode()); + TC_LOG_ERROR("maps", "InstanceMap::CannotEnter - player %s(%s) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetId(), GetInstanceId(), GetSpawnMode()); ABORT(); - return false; + return CANNOT_ENTER_ALREADY_IN_MAP; } // allow GM's to enter if (player->IsGameMaster()) - return Map::CanEnter(player); + return Map::CannotEnter(player); // cannot enter if the instance is full (player cap), GMs don't count uint32 maxPlayers = GetMaxPlayers(); if (GetPlayersCountExceptGMs() >= maxPlayers) { TC_LOG_WARN("maps", "MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str()); - player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); - return false; + return CANNOT_ENTER_MAX_PLAYERS; } - // cannot enter while an encounter is in progress - // allow if just loading + // cannot enter while an encounter is in progress (unless this is a relog, in which case it is permitted) if (!player->IsLoading() && IsRaid() && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress()) - { - player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); - return false; - } + return CANNOT_ENTER_ZONE_IN_COMBAT; - // cannot enter if instance is in use by another party/soloer that have a - // permanent save in the same instance id + // cannot enter if player is permanent saved to a different instance id + if (InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), GetDifficultyID())) + if (playerBind->perm && playerBind->save) + if (playerBind->save->GetInstanceId() != GetInstanceId()) + return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; - PlayerList const &playerList = GetPlayers(); - - if (!playerList.isEmpty()) - for (PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i) - if (Player* iPlayer = i->GetSource()) - { - if (iPlayer->IsGameMaster()) // bypass GMs - continue; - if (!player->GetGroup()) // player has not group and there is someone inside, deny entry - { - player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); - return false; - } - // player inside instance has no group or his groups is different to entering player's one, deny entry - if (!iPlayer->GetGroup() || iPlayer->GetGroup() != player->GetGroup()) - { - player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); - return false; - } - break; - } - - return Map::CanEnter(player); + return Map::CannotEnter(player); } /* @@ -3474,21 +3450,21 @@ void BattlegroundMap::InitVisibilityDistance() m_VisibilityNotifyPeriod = World::GetVisibilityNotifyPeriodInBGArenas(); } -bool BattlegroundMap::CanEnter(Player* player) +Map::EnterState BattlegroundMap::CannotEnter(Player* player) { if (player->GetMapRef().getTarget() == this) { - TC_LOG_ERROR("maps", "BGMap::CanEnter - %s is already in map!", player->GetGUID().ToString().c_str()); + TC_LOG_ERROR("maps", "BGMap::CannotEnter - player %s is already in map!", player->GetGUID().ToString().c_str()); ABORT(); - return false; + return CANNOT_ENTER_ALREADY_IN_MAP; } if (player->GetBattlegroundId() != GetInstanceId()) - return false; + return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; // player number limit is checked in bgmgr, no need to do it here - return Map::CanEnter(player); + return Map::CannotEnter(player); } bool BattlegroundMap::AddPlayerToMap(Player* player, bool initPlayer /*= true*/) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 34aa00e4bf9..d51f05bc32a 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -365,7 +365,23 @@ class Map : public GridRefManager<NGridType> uint32 GetInstanceId() const { return i_InstanceId; } uint8 GetSpawnMode() const { return (i_spawnMode); } - virtual bool CanEnter(Player* /*player*/) { return true; } + + enum EnterState + { + CAN_ENTER = 0, + CANNOT_ENTER_ALREADY_IN_MAP = 1, // Player is already in the map + CANNOT_ENTER_NO_ENTRY, // No map entry was found for the target map ID + CANNOT_ENTER_UNINSTANCED_DUNGEON, // No instance template was found for dungeon map + CANNOT_ENTER_DIFFICULTY_UNAVAILABLE, // Requested instance difficulty is not available for target map + CANNOT_ENTER_NOT_IN_RAID, // Target instance is a raid instance and the player is not in a raid group + CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE, // Player is dead and their corpse is not in target instance + CANNOT_ENTER_INSTANCE_BIND_MISMATCH, // Player's permanent instance save is not compatible with their group's current instance bind + CANNOT_ENTER_TOO_MANY_INSTANCES, // Player has entered too many instances recently + CANNOT_ENTER_MAX_PLAYERS, // Target map already has the maximum number of players allowed + CANNOT_ENTER_ZONE_IN_COMBAT, // A boss encounter is currently in progress on the target map + CANNOT_ENTER_UNSPECIFIED_REASON + }; + virtual EnterState CannotEnter(Player* /*player*/) { return CAN_ENTER; } const char* GetMapName() const; // have meaning only for instanced map (that have set real difficulty) @@ -751,7 +767,7 @@ class InstanceMap : public Map InstanceScript* GetInstanceScript() { return i_data; } void PermBindAllPlayers(Player* source); void UnloadAll() override; - bool CanEnter(Player* player) override; + EnterState CannotEnter(Player* player) override; void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); @@ -774,7 +790,7 @@ class BattlegroundMap : public Map bool AddPlayerToMap(Player* player, bool initPlayer = true) override; void RemovePlayerFromMap(Player*, bool) override; - bool CanEnter(Player* player) override; + EnterState CannotEnter(Player* player) override; void SetUnload(); //void UnloadAll(bool pForce); void RemoveAllPlayers() override; diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index 70623e1d7ab..4c065f836b9 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -111,12 +111,12 @@ void MapInstanced::UnloadAll() - 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(const uint32 mapId, Player* player) +Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player, uint32 loginInstanceId) { if (GetId() != mapId || !player) - return NULL; + return nullptr; - Map* map = NULL; + Map* map = nullptr; uint32 newInstanceId = 0; // instanceId of the resulting map if (IsBattlegroundOrArena()) @@ -125,7 +125,7 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) // the instance id is set in battlegroundid newInstanceId = player->GetBattlegroundId(); if (!newInstanceId) - return NULL; + return nullptr; map = sMapMgr->FindMap(mapId, newInstanceId); if (!map) @@ -135,20 +135,29 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) else { player->TeleportToBGEntryPoint(); - return NULL; + return nullptr; } } } else if (!IsGarrison()) { InstancePlayerBind* pBind = player->GetBoundInstance(GetId(), player->GetDifficultyID(GetEntry())); - InstanceSave* pSave = pBind ? pBind->save : NULL; + InstanceSave* pSave = pBind ? pBind->save : nullptr; - // the player's permanent player bind is taken into consideration first - // then the player's group bind and finally the solo bind. + // 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) { - InstanceGroupBind* groupBind = NULL; + 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); + return (map && map->GetId() == GetId()) ? map : nullptr; // is this check necessary? or does MapInstanced only find instances of itself? + } + + 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) @@ -288,8 +297,8 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) return true; } -bool MapInstanced::CanEnter(Player* /*player*/) +Map::EnterState MapInstanced::CannotEnter(Player* /*player*/) { //ABORT(); - return true; + return CAN_ENTER; } diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h index f0d185bd796..4fb334cb0ce 100644 --- a/src/server/game/Maps/MapInstanced.h +++ b/src/server/game/Maps/MapInstanced.h @@ -39,13 +39,13 @@ class MapInstanced : public Map void DelayedUpdate(const uint32 diff) override; //void RelocationNotify(); void UnloadAll() override; - bool CanEnter(Player* player) override; + EnterState CannotEnter(Player* /*player*/) override; - Map* CreateInstanceForPlayer(const uint32 mapId, Player* player); + Map* CreateInstanceForPlayer(const 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() ? NULL : i->second); + return(i == m_InstancedMaps.end() ? nullptr : i->second); } bool DestroyInstance(InstancedMaps::iterator &itr); diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 81302671762..184af8fc360 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -103,12 +103,12 @@ Map* MapManager::FindBaseNonInstanceMap(uint32 mapId) const return map; } -Map* MapManager::CreateMap(uint32 id, Player* player) +Map* MapManager::CreateMap(uint32 id, Player* player, uint32 loginInstanceId) { Map* m = CreateBaseMap(id); if (m && m->Instanceable()) - m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player); + m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player, loginInstanceId); return m; } @@ -125,47 +125,36 @@ Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const return ((MapInstanced*)map)->FindInstanceMap(instanceId); } -bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) +Map::EnterState MapManager::PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck) { MapEntry const* entry = sMapStore.LookupEntry(mapid); if (!entry) - return false; + return Map::CANNOT_ENTER_NO_ENTRY; if (!entry->IsDungeon()) - return true; + return Map::CAN_ENTER; InstanceTemplate const* instance = sObjectMgr->GetInstanceTemplate(mapid); if (!instance) - return false; + return Map::CANNOT_ENTER_UNINSTANCED_DUNGEON; 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 = GetDownscaledMapDifficultyData(entry->ID, targetDifficulty); if (!mapDiff) - { - player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, requestedDifficulty); - return false; - } + return Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE; //Bypass checks for GMs if (player->IsGameMaster()) - return true; + return Map::CAN_ENTER; char const* mapName = entry->MapName_lang; Group* group = player->GetGroup(); - if (entry->IsRaid()) - { - // can only enter in a raid group + if (entry->IsRaid()) // can only enter in a raid group if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID)) - { - /// @todo this is not a good place to send the message - player->SendRaidGroupOnlyMessage(RAID_GROUP_ERR_ONLY, 0); - TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName); - return false; - } - } + return Map::CANNOT_ENTER_NOT_IN_RAID; if (!player->IsAlive()) { @@ -183,12 +172,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) } while (corpseMap); if (!corpseMap) - { - WorldPackets::Misc::AreaTriggerNoCorpse packet; - player->GetSession()->SendPacket(packet.Write()); - TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance '%s' and cannot enter.", player->GetName().c_str(), mapName); - return false; - } + return Map::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); } @@ -197,23 +181,13 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) } //Get instance where player's group is bound & its map - if (group) + if (!loginCheck && group) { InstanceGroupBind* boundInstance = group->GetBoundInstance(entry); if (boundInstance && boundInstance->save) if (Map* boundMap = sMapMgr->FindMap(mapid, boundInstance->save->GetInstanceId())) - if (!loginCheck && !boundMap->CanEnter(player)) - return false; - /* - This check has to be moved to InstanceMap::CanEnter() - // Player permanently bounded to different instance than groups one - InstancePlayerBind* playerBoundedInstance = player->GetBoundInstance(mapid, player->GetDifficultyID(entry)); - if (playerBoundedInstance && playerBoundedInstance->perm && playerBoundedInstance->save && - boundedInstance->save->GetInstanceId() != playerBoundedInstance->save->GetInstanceId()) - { - /// @todo send some kind of error message to the player - return false; - }*/ + if (Map::EnterState denyReason = boundMap->CannotEnter(player)) + return denyReason; } // players are only allowed to enter 5 instances per hour @@ -225,14 +199,14 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) // instanceId can never be 0 - will not be found if (!player->CheckInstanceCount(instanceIdToCheck) && !player->isDead()) - { - player->SendTransferAborted(mapid, TRANSFER_ABORT_TOO_MANY_INSTANCES); - return false; - } + return Map::CANNOT_ENTER_TOO_MANY_INSTANCES; } //Other requirements - return player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true); + if (player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true)) + return Map::CAN_ENTER; + else + return Map::CANNOT_ENTER_UNSPECIFIED_REASON; } void MapManager::Update(uint32 diff) diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 24cbbf4ef4d..5d679cbe4d9 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -35,7 +35,7 @@ class MapManager Map* CreateBaseMap(uint32 mapId); Map* FindBaseNonInstanceMap(uint32 mapId) const; - Map* CreateMap(uint32 mapId, Player* player); + Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId=0); Map* FindMap(uint32 mapId, uint32 instanceId) const; uint32 GetAreaId(uint32 mapid, float x, float y, float z) const @@ -102,7 +102,7 @@ class MapManager void DoDelayedMovesAndRemoves(); - bool CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck = false); + Map::EnterState PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck = false); void InitializeVisibilityDistanceInfo(); /* statistics */ diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 76d7a1380bf..4ca67fbadfc 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1212,8 +1212,8 @@ enum TrinityStrings LANG_NPCINFO_INHABIT_TYPE = 11008, LANG_NPCINFO_FLAGS_EXTRA = 11009, + LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010, - LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010 - + LANG_INSTANCE_BIND_MISMATCH = 11014 }; #endif |