From 27f0edde0f9800e1d1703db4e8a16980d289f455 Mon Sep 17 00:00:00 2001 From: treeston Date: Sun, 3 Jan 2016 14:38:36 +0100 Subject: [PATCH] Game/Maps: Instance handling follow-up: - Fix a bug where a player could maintain a conflicting non-perm solo bind if they were in the instance when invited to group. Closes #16150. - If a group is created while the leader is in an instance that nobody is bound to, the group will take over the instance and bind to it. This stops the homebind timer when reforming group after disconnects and the like. (cherry picked from commit 0f0a51b87a1c8c4c0809b9328a3ddb606b138c4b) --- src/server/game/Entities/Player/Player.cpp | 25 ------------ src/server/game/Entities/Player/Player.h | 1 - src/server/game/Groups/Group.cpp | 45 +++++++++++++++++++--- src/server/game/Groups/Group.h | 1 + src/server/game/Maps/Map.cpp | 2 +- src/server/game/Maps/MapInstanced.cpp | 4 ++ 6 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0402cc2a206..fc582e02d94 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -19132,31 +19132,6 @@ void Player::SendSavedInstances() } } -/// convert the player's binds to the group -void Player::ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader) -{ - // copy all binds to the group, when changing leader it's assumed the character - // will not have any solo binds - - for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) - { - for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) - { - if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first)) - group->BindToInstance(itr->second.save, itr->second.perm, false); - - // permanent binds are not removed - if (switchLeader && !itr->second.perm) - { - // increments itr in call - player->UnbindInstance(itr, Difficulty(i), false); - } - else - ++itr; - } - } -} - bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report) { if (!IsGameMaster() && ar) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 672ce2a4746..a2602bb94c0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2406,7 +2406,6 @@ class Player : public Unit, public GridObject bool HasPendingBind() const { return _pendingBindId > 0; } void SendRaidInfo(); void SendSavedInstances(); - static void ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader); bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false); bool CheckInstanceValidity(bool /*isLogin*/); bool CheckInstanceCount(uint32 instanceId) const; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index c86057a9ac1..565952caf0f 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -148,10 +148,9 @@ bool Group::Create(Player* leader) CharacterDatabase.Execute(stmt); + Group::ConvertLeaderInstancesToGroup(leader, this, false); ASSERT(AddMember(leader)); // If the leader can't be added to a new group because it appears full, something is clearly wrong. - - Player::ConvertInstancesToGroup(leader, this, false); } else if (!AddMember(leader)) return false; @@ -406,9 +405,7 @@ bool Group::AddMember(Player* player) player->SetGroup(this, subGroup); // if the same group invites the player back, cancel the homebind timer - InstanceGroupBind* bind = GetBoundInstance(player); - if (bind && bind->save->GetInstanceId() == player->GetInstanceId()) - player->m_InstanceValid = true; + player->m_InstanceValid = player->CheckInstanceValidity(false); if (!isRaidGroup()) // reset targetIcons for non-raid-groups { @@ -685,7 +682,7 @@ void Group::ChangeLeader(ObjectGuid newLeaderGuid) } // Copy the permanent binds from the new leader to the group - Player::ConvertInstancesToGroup(newLeader, this, true); + Group::ConvertLeaderInstancesToGroup(newLeader, this, true); // Update the group leader PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER); @@ -711,6 +708,42 @@ void Group::ChangeLeader(ObjectGuid newLeaderGuid) BroadcastPacket(&data, true); } +/// convert the player's binds to the group +void Group::ConvertLeaderInstancesToGroup(Player* player, Group* group, bool switchLeader) +{ + // copy all binds to the group, when changing leader it's assumed the character + // will not have any solo binds + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { + for (Player::BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) + { + if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first)) + group->BindToInstance(itr->second.save, itr->second.perm, false); + + // permanent binds are not removed + if (switchLeader && !itr->second.perm) + { + // increments itr in call + player->UnbindInstance(itr, Difficulty(i), false); + } + else + ++itr; + } + } + + /* if group leader is in a non-raid dungeon map and nobody is actually bound to this map then the group can "take over" the instance * + * (example: two-player group disbanded by disconnect where the player reconnects within 60 seconds and the group is reformed) */ + if (Map* playerMap = player->GetMap()) + if (!switchLeader && playerMap->IsNonRaidDungeon()) + if (InstanceSave* save = sInstanceSaveMgr->GetInstanceSave(playerMap->GetInstanceId())) + if (save->GetGroupCount() == 0 && save->GetPlayerCount() == 0) + { + TC_LOG_DEBUG("maps", "Group::ConvertLeaderInstancesToGroup: Group for player %s is taking over unbound instance map %d with Id %d", player->GetName().c_str(), playerMap->GetId(), playerMap->GetInstanceId()); + // if nobody is saved to this, then the save wasn't permanent + group->BindToInstance(save, false, false); + } +} + void Group::Disband(bool hideDestroy /* = false */) { sScriptMgr->OnGroupDisband(this); diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index 40d796f043d..0595782742d 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -198,6 +198,7 @@ class Group bool AddMember(Player* player); bool RemoveMember(ObjectGuid guid, const RemoveMethod &method = GROUP_REMOVEMETHOD_DEFAULT, ObjectGuid kicker = ObjectGuid::Empty, const char* reason = NULL); void ChangeLeader(ObjectGuid guid); + static void ConvertLeaderInstancesToGroup(Player* player, Group* group, bool switchLeader); void SetLootMethod(LootMethod method); void SetLooterGuid(ObjectGuid guid); void SetMasterLooterGuid(ObjectGuid guid); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index d839f550405..a9f3710a385 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3031,7 +3031,7 @@ bool InstanceMap::AddPlayerToMap(Player* player) { if (group) { - // solo saves should be reset when entering a group + // solo saves should have been reset when the map was loaded InstanceGroupBind* groupBind = group->GetBoundInstance(this); if (playerBind && playerBind->save != mapSave) { diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index f8fa36a20ea..d83d88ba87c 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -163,7 +163,11 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player, u { groupBind = group->GetBoundInstance(this); if (groupBind) + { + // solo saves should be reset when entering a group's instance + player->UnbindInstance(GetId(), player->GetDifficulty(IsRaid())); pSave = groupBind->save; + } } } if (pSave)