diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2e3f07ab3..fe27c8095 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1435,10 +1435,10 @@ 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 (!(options & TELE_TO_GM_MODE) && !sMapMgr->CanPlayerEnter(mapid, this, false)) + if (!(options & TELE_TO_GM_MODE) && sMapMgr->PlayerCannotEnter(mapid, this, false)) return false; - // if CanPlayerEnter -> CanEnter: checked above + // if PlayerCannotEnter -> CanEnter: checked above { //lets reset near teleport flag if it wasn't reset during chained teleports SetSemaphoreTeleportNear(0); diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 0047e68d1..58aa260fc 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -6971,12 +6971,12 @@ bool Player::CheckInstanceLoginValid() return false; } - // pussywizard: check CanEnter for GetMap(), because in CanPlayerEnter it is called for a map decided before loading screen (can change) - if (!GetMap()->CanEnter(this, true)) + // pussywizard: check CanEnter for GetMap(), because in PlayerCannotEnter it is called for a map decided before loading screen (can change) + if (GetMap()->CannotEnter(this, true)) return false; // do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player - return sMapMgr->CanPlayerEnter(GetMap()->GetId(), this, true); + return sMapMgr->PlayerCannotEnter(GetMap()->GetId(), this, true) == Map::CAN_ENTER; } bool Player::CheckInstanceCount(uint32 instanceId) const diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index e84119c8b..0483599a9 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -771,8 +771,36 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data) 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_NOT_IN_RAID: + case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH: + case Map::CANNOT_ENTER_TOO_MANY_INSTANCES: + case Map::CANNOT_ENTER_MAX_PLAYERS: + case Map::CANNOT_ENTER_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 ea2971309..5b2fb4aa2 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -77,7 +77,7 @@ void WorldSession::HandleMoveWorldportAck() Map* newMap = sMapMgr->CreateMap(loc.GetMapId(), GetPlayer()); // the CanEnter 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(), false)) + if (!newMap || newMap->CannotEnter(GetPlayer(), false)) { LOG_ERROR("network.opcode", "Map %d could not be created for player %s, porting player to homebind", loc.GetMapId(), GetPlayer()->GetGUID().ToString().c_str()); GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index fae5185cf..6217000c7 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2657,19 +2657,19 @@ void InstanceMap::InitVisibilityDistance() /* Do map specific checks to see if the player can enter */ -bool InstanceMap::CanEnter(Player* player, bool loginCheck) +Map::EnterState InstanceMap::CannotEnter(Player* player, bool loginCheck) { if (!loginCheck && player->GetMapRef().getTarget() == this) { 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()); ABORT(); - return false; + return CANNOT_ENTER_ALREADY_IN_MAP; } // allow GM's to enter if (player->IsGameMaster()) - return Map::CanEnter(player, loginCheck); + return Map::CannotEnter(player, loginCheck); // cannot enter if the instance is full (player cap), GMs don't count uint32 maxPlayers = GetMaxPlayers(); @@ -2677,7 +2677,7 @@ bool InstanceMap::CanEnter(Player* player, bool loginCheck) { LOG_DEBUG("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 on raids @@ -2685,7 +2685,7 @@ bool InstanceMap::CanEnter(Player* player, bool loginCheck) if (checkProgress && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress()) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); - return false; + return CANNOT_ENTER_ZONE_IN_COMBAT; } // xinef: dont allow LFG Group to enter other instance that is selected @@ -2694,7 +2694,7 @@ bool InstanceMap::CanEnter(Player* player, bool loginCheck) if (!sLFGMgr->inLfgDungeonMap(group->GetGUID(), GetId(), GetDifficulty())) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAP_NOT_ALLOWED); - return false; + return CANNOT_ENTER_UNSPECIFIED_REASON; } // cannot enter if instance is in use by another party/soloer that have a permanent save in the same instance id @@ -2710,18 +2710,18 @@ bool InstanceMap::CanEnter(Player* player, bool loginCheck) if (!player->GetGroup()) // player has not group and there is someone inside, deny entry { player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); - return false; + return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; } // 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; + return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; } break; } - return Map::CanEnter(player, loginCheck); + return Map::CannotEnter(player, loginCheck); } /* @@ -3020,21 +3020,21 @@ void BattlegroundMap::InitVisibilityDistance() m_VisibleDistance = 30.0f; } -bool BattlegroundMap::CanEnter(Player* player, bool loginCheck) +Map::EnterState BattlegroundMap::CannotEnter(Player* player, bool loginCheck) { if (!loginCheck && player->GetMapRef().getTarget() == this) { LOG_ERROR("maps", "BGMap::CanEnter - 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; // pussywizard: no need to check player limit here, invitations are limited by Battleground::GetFreeSlotsForTeam - return Map::CanEnter(player, loginCheck); + return Map::CannotEnter(player, loginCheck); } bool BattlegroundMap::AddPlayerToMap(Player* player) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 4efcb8deb..ae7f77faa 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -377,7 +377,25 @@ public: [[nodiscard]] uint32 GetInstanceId() const { return i_InstanceId; } [[nodiscard]] uint8 GetSpawnMode() const { return (i_spawnMode); } - virtual bool CanEnter(Player* /*player*/, bool /*loginCheck = false*/) { 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*/, bool /*loginCheck = false*/) { return CAN_ENTER; } + [[nodiscard]] const char* GetMapName() const; // have meaning only for instanced map (that have set real difficulty) @@ -756,7 +774,7 @@ public: [[nodiscard]] InstanceScript const* GetInstanceScript() const { return instance_data; } void PermBindAllPlayers(); void UnloadAll() override; - bool CanEnter(Player* player, bool loginCheck = false) override; + EnterState CannotEnter(Player* player, bool loginCheck = false) override; void SendResetWarnings(uint32 timeLeft) const; [[nodiscard]] uint32 GetMaxPlayers() const; @@ -778,7 +796,7 @@ public: bool AddPlayerToMap(Player*) override; void RemovePlayerFromMap(Player*, bool) override; - bool CanEnter(Player* player, bool loginCheck = false) override; + EnterState CannotEnter(Player* player, bool loginCheck = false) 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 066b35069..3bb9c612a 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -266,8 +266,8 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator& itr) return true; } -bool MapInstanced::CanEnter(Player* /*player*/, bool /*loginCheck*/) +Map::EnterState MapInstanced::CannotEnter(Player* /*player*/, bool /*loginCheck*/) { //ABORT(); - return true; + return CAN_ENTER; } diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h index 979834a12..d39fade5f 100644 --- a/src/server/game/Maps/MapInstanced.h +++ b/src/server/game/Maps/MapInstanced.h @@ -25,7 +25,7 @@ public: void DelayedUpdate(const uint32 diff) override; //void RelocationNotify(); void UnloadAll() override; - bool CanEnter(Player* player, bool loginCheck = false) override; + EnterState CannotEnter(Player* player, bool loginCheck = false) override; Map* CreateInstanceForPlayer(const uint32 mapId, Player* player); Map* FindInstanceMap(uint32 instanceId) const diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index c6285f1f5..72d1db8bb 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -122,18 +122,18 @@ 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->GetDifficulty(entry->IsRaid()); @@ -142,17 +142,17 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) 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->name[player->GetSession()->GetSessionDbcLocale()]; if (!sScriptMgr->CanEnterMap(player, entry, instance, mapDiff, loginCheck)) - return false; + return Map::CANNOT_ENTER_UNSPECIFIED_REASON; Group* group = player->GetGroup(); if (entry->IsRaid()) @@ -164,7 +164,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) // TODO: this is not a good place to send the message player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetAcoreString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName); 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; } } @@ -174,7 +174,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) if (!sLFGMgr->inLfgDungeonMap(group->GetGUID(), mapid, targetDifficulty)) { player->SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED); - return false; + return Map::CANNOT_ENTER_UNSPECIFIED_REASON; } if (!player->IsAlive()) @@ -197,13 +197,13 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE, 0); player->GetSession()->SendPacket(&data); 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; } LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName); } else { - LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); + LOG_DEBUG("maps", "Map::PlayerCannotEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); } } @@ -213,8 +213,8 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) uint32 destInstId = sInstanceSaveMgr->PlayerGetDestinationInstanceId(player, mapid, targetDifficulty); if (destInstId) if (Map* boundMap = sMapMgr->FindMap(mapid, destInstId)) - if (!boundMap->CanEnter(player, loginCheck)) - return false; + if (Map::EnterState denyReason = boundMap->CannotEnter(player, loginCheck)) + return denyReason; } // players are only allowed to enter 5 instances per hour @@ -228,12 +228,12 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) if (!player->CheckInstanceCount(instaceIdToCheck) && !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); + return player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true) ? Map::CAN_ENTER : 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 127ce2a9a..fe7f76d2c 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -119,7 +119,7 @@ public: 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/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index f46944718..781d6e05e 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1191,7 +1191,7 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) uint32 mapid = destTarget->GetMapId(); float x, y, z, orientation; destTarget->GetPosition(x, y, z, orientation); - target->TeleportTo(mapid, x, y, z, orientation, TELE_TO_GM_MODE); // skip CanPlayerEnter check + target->TeleportTo(mapid, x, y, z, orientation, TELE_TO_GM_MODE); // skip PlayerCannotEnter check } return; }