diff --git a/src/game/Entities/Player.cpp b/src/game/Entities/Player.cpp index 61459d79d4b..41672c14b4e 100644 --- a/src/game/Entities/Player.cpp +++ b/src/game/Entities/Player.cpp @@ -18174,6 +18174,9 @@ void Player::SendResetInstanceSuccess(uint32 MapId) const void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId) const { // TODO: find what other fail reasons there are besides players in the instance + // 0 - at least one player is in the instance + // 1 - at least one player is offline + // 2 - at least one player try to enter the instance (being teleported in) WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4); data << uint32(reason); data << uint32(MapId); diff --git a/src/game/Globals/ObjectMgr.cpp b/src/game/Globals/ObjectMgr.cpp index 0b32f9a9f91..b71aa59c2e4 100644 --- a/src/game/Globals/ObjectMgr.cpp +++ b/src/game/Globals/ObjectMgr.cpp @@ -1759,6 +1759,25 @@ void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data } } +// Get player map id of offline player. Return -1 if not found! +int32 ObjectMgr::GetPlayerMapIdByGUID(ObjectGuid const& guid) const +{ + // prevent DB access for online player + if (Player* player = GetPlayer(guid)) + return int32(player->GetMapId()); + + QueryResult* result = CharacterDatabase.PQuery("SELECT map FROM characters WHERE guid = '%u'", guid.GetCounter()); + + if (result) + { + uint32 mapId = (*result)[0].GetUInt32(); + delete result; + return int32(mapId); + } + + return -1; +} + // name must be checked to correctness (if received) before call this function ObjectGuid ObjectMgr::GetPlayerGuidByName(std::string name) const { diff --git a/src/game/Globals/ObjectMgr.h b/src/game/Globals/ObjectMgr.h index faa5a0a19c9..4e5095f4dc0 100644 --- a/src/game/Globals/ObjectMgr.h +++ b/src/game/Globals/ObjectMgr.h @@ -553,6 +553,7 @@ class ObjectMgr void GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const; ObjectGuid GetPlayerGuidByName(std::string name) const; + int32 GetPlayerMapIdByGUID(ObjectGuid const& guid) const; bool GetPlayerNameByGUID(ObjectGuid guid, std::string& name) const; Team GetPlayerTeamByGUID(ObjectGuid guid) const; uint32 GetPlayerAccountIdByGUID(ObjectGuid guid) const; diff --git a/src/game/Groups/Group.cpp b/src/game/Groups/Group.cpp index 5056be38ead..9a7d9e9833a 100644 --- a/src/game/Groups/Group.cpp +++ b/src/game/Groups/Group.cpp @@ -198,6 +198,15 @@ bool Group::LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant) member.group = subgroup; member.assistant = assistant; + + int32 lastMap = sObjectMgr.GetPlayerMapIdByGUID(member.guid); + if (lastMap < 0) + { + sLog.outError("Group::LoadMemberFromDB> MapId is not found for %s.", member.guid.GetString().c_str()); + return false; + } + member.lastMap = uint32(lastMap); + m_memberSlots.push_back(member); SubGroupCounterIncrease(subgroup); @@ -1211,16 +1220,27 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send // we assume that when the difficulty changes, all instances that can be reset will be Difficulty diff = GetDifficulty(isRaid); - typedef std::set OfflineMapSet; - OfflineMapSet mapsWithOfflinePlayer; // to store map of offline players + typedef std::set Uint32Set; + Uint32Set mapsWithOfflinePlayer; // to store map of offline players + Uint32Set mapsWithBeingTeleportedPlayer; // to store map of offline players if (method != INSTANCE_RESET_GROUP_DISBAND) { // Store maps in which are offline members for instance reset check. for (member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr) { - if (!ObjectAccessor::FindPlayer(itr->guid)) - mapsWithOfflinePlayer.insert(itr->lastMap); // add last map from offline player + Player* plr = ObjectAccessor::FindPlayer(itr->guid); + if (!plr) + { + // add last map from offline player + mapsWithOfflinePlayer.insert(itr->lastMap); + } + else + { + // add teleport destination map + if (plr->IsBeingTeleported()) + mapsWithBeingTeleportedPlayer.insert(plr->GetTeleportDest().mapid); + } } } @@ -1244,13 +1264,27 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send } } - bool isEmpty = true; // check if there are offline members on the map if (method != INSTANCE_RESET_GROUP_DISBAND && mapsWithOfflinePlayer.find(state->GetMapId()) != mapsWithOfflinePlayer.end()) - isEmpty = false; + { + if (SendMsgTo) + SendMsgTo->SendResetInstanceFailed(1, state->GetMapId()); + ++itr; + continue; + } + + // check if there are teleporting members to the map + if (method != INSTANCE_RESET_GROUP_DISBAND && mapsWithBeingTeleportedPlayer.find(state->GetMapId()) != mapsWithBeingTeleportedPlayer.end()) + { + if (SendMsgTo) + SendMsgTo->SendResetInstanceFailed(2, state->GetMapId()); + ++itr; + continue; + } + bool isEmpty = true; // if the map is loaded, reset it if can - if (isEmpty && entry->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !state->CanReset())) + if (entry->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !state->CanReset())) if (Map* map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId())) isEmpty = ((DungeonMap*)map)->Reset(method);