aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2015-12-31 19:51:07 +0100
committerShauren <shauren.trinity@gmail.com>2016-03-21 19:41:28 +0100
commit7422557d03b3cbb5b00a39b16c52f879c2f31fdd (patch)
treec379a50a7ba112b8a4f5149f6c89973939a8675d
parent96ca739a0b0d2b34a28fbac0593e076fb3c42f1b (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.sql4
-rw-r--r--src/server/game/Entities/Player/Player.cpp101
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp63
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp19
-rw-r--r--src/server/game/Instances/InstanceScript.h2
-rw-r--r--src/server/game/Maps/Map.cpp60
-rw-r--r--src/server/game/Maps/Map.h22
-rw-r--r--src/server/game/Maps/MapInstanced.cpp31
-rw-r--r--src/server/game/Maps/MapInstanced.h6
-rw-r--r--src/server/game/Maps/MapManager.cpp64
-rw-r--r--src/server/game/Maps/MapManager.h4
-rw-r--r--src/server/game/Miscellaneous/Language.h4
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