fix(Core/LFG): prevent RDF anti-kick exploit via loot rolls (#25112)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Andrew 2026-03-28 18:16:51 -03:00 committed by GitHub
parent 82eaf7506b
commit 3f0c60c1dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 30 deletions

View file

@ -13040,7 +13040,7 @@ PartyResult Player::CanUninviteFromGroup(ObjectGuid targetPlayerGUID) const
if (state == lfg::LFG_STATE_FINISHED_DUNGEON)
return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;
if (grp->isRollLootActive())
if (grp->isRollLootActive() && ObjectAccessor::FindConnectedPlayer(targetPlayerGUID))
return ERR_PARTY_LFG_BOOT_LOOT_ROLLS;
/// @todo: Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.

View file

@ -600,35 +600,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R
}
// Remove player from loot rolls
for (Rolls::iterator it = RollId.begin(); it != RollId.end();)
{
Roll* roll = *it;
Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
if (itr2 == roll->playerVote.end())
{
++it;
continue;
}
if (itr2->second == GREED || itr2->second == DISENCHANT)
--roll->totalGreed;
else if (itr2->second == NEED)
--roll->totalNeed;
else if (itr2->second == PASS)
--roll->totalPass;
if (itr2->second != NOT_VALID)
--roll->totalPlayersRolling;
roll->playerVote.erase(itr2);
// Xinef: itr can be erased inside
// Xinef: player is removed from all vote lists so it will not pass above playerVote == playerVote.end statement during second iteration
if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE))
it = RollId.begin();
else
++it;
}
RemovePlayerFromRolls(guid);
// Update subgroups
member_witerator slot = _getMemberWSlot(guid);
@ -1422,6 +1394,37 @@ void Group::EndRoll(Loot* pLoot, Map* allowedMap)
}
}
void Group::RemovePlayerFromRolls(ObjectGuid guid)
{
for (Rolls::iterator it = RollId.begin(); it != RollId.end();)
{
Roll* roll = *it;
Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid);
if (itr2 == roll->playerVote.end())
{
++it;
continue;
}
if (itr2->second == GREED || itr2->second == DISENCHANT)
--roll->totalGreed;
else if (itr2->second == NEED)
--roll->totalNeed;
else if (itr2->second == PASS)
--roll->totalPass;
if (itr2->second != NOT_VALID)
--roll->totalPlayersRolling;
roll->playerVote.erase(itr2);
if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE))
it = RollId.begin();
else
++it;
}
}
void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
{
Roll* roll = *rollI;

View file

@ -299,6 +299,7 @@ public:
void CountTheRoll(Rolls::iterator roll, Map* allowedMap);
bool CountRollVote(ObjectGuid playerGUID, ObjectGuid Guid, uint8 Choise);
void EndRoll(Loot* loot, Map* allowedMap);
void RemovePlayerFromRolls(ObjectGuid guid);
// related to disenchant rolls
void ResetMaxEnchantingLevel();

View file

@ -702,6 +702,10 @@ void WorldSession::LogoutPlayer(bool save)
// a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) d) LeaveGroupOnLogout is enabled
if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && !_player->GetGroup()->isLFGGroup() && m_Socket && sWorld->getBoolConfig(CONFIG_LEAVE_GROUP_ON_LOGOUT))
_player->RemoveFromGroup();
// Remove player from active loot rolls in LFG groups (player stays in group but should not block rolls)
else if (Group* group = _player->GetGroup())
if (group->isLFGGroup())
group->RemovePlayerFromRolls(_player->GetGUID());
// pussywizard: checked second time after being removed from a group
if (!_player->IsBeingTeleportedFar() && !_player->m_InstanceValid && !_player->IsGameMaster())