fix(Core/Player): prevent crash exploit spam duel starting (#9226)

This commit is contained in:
IntelligentQuantum
2021-11-28 23:12:53 +03:30
committed by GitHub
parent 25ca425357
commit ab761c9865
8 changed files with 144 additions and 112 deletions

View File

@@ -211,8 +211,6 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
m_auraRaidUpdateMask = 0;
m_bPassOnGroupLoot = false;
duel = nullptr;
m_GuildIdInvited = 0;
m_ArenaTeamIdInvited = 0;
@@ -6154,18 +6152,20 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid)
void Player::CheckDuelDistance(time_t currTime)
{
if (!duel)
{
return;
}
ObjectGuid duelFlagGUID = GetGuidValue(PLAYER_DUEL_ARBITER);
GameObject* obj = GetMap()->GetGameObject(duelFlagGUID);
if (!obj)
return;
if (duel->outOfBound == 0)
if (!duel->OutOfBoundsTime)
{
if (!IsWithinDistInMap(obj, 50))
{
duel->outOfBound = currTime;
duel->OutOfBoundsTime = currTime + 10;
WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
GetSession()->SendPacket(&data);
@@ -6175,12 +6175,12 @@ void Player::CheckDuelDistance(time_t currTime)
{
if (IsWithinDistInMap(obj, 40))
{
duel->outOfBound = 0;
duel->OutOfBoundsTime = 0;
WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
GetSession()->SendPacket(&data);
}
else if (currTime >= (duel->outOfBound + 10))
else if (currTime >= duel->OutOfBoundsTime)
DuelComplete(DUEL_FLED);
}
}
@@ -6193,57 +6193,76 @@ bool Player::IsOutdoorPvPActive()
void Player::DuelComplete(DuelCompleteType type)
{
// duel not requested
if (!duel || !duel->opponent || !duel->initiator)
if (!duel)
return;
LOG_DEBUG("entities.unit", "Duel Complete %s %s", GetName().c_str(), duel->opponent->GetName().c_str());
// Check if DuelComplete() has been called already up in the stack and in that case don't do anything else here
if (duel->State == DUEL_STATE_COMPLETED)
return;
Player* opponent = duel->Opponent;
duel->State = DUEL_STATE_COMPLETED;
opponent->duel->State = DUEL_STATE_COMPLETED;
LOG_DEBUG("entities.unit", "Player::DuelComplete: Player '%s' (%s), Opponent: '%s' (%s)",
GetName().c_str(), GetGUID().ToString().c_str(), opponent->GetName().c_str(), opponent->GetGUID().ToString().c_str());
WorldPacket data(SMSG_DUEL_COMPLETE, (1));
data << (uint8)((type != DUEL_INTERRUPTED) ? 1 : 0);
GetSession()->SendPacket(&data);
duel->opponent->GetSession()->SendPacket(&data);
data << uint8((type != DUEL_INTERRUPTED) ? 1 : 0);
SendDirectMessage(&data);
if (opponent->GetSession())
{
opponent->SendDirectMessage(&data);
}
if (type != DUEL_INTERRUPTED)
{
data.Initialize(SMSG_DUEL_WINNER, (1 + 20)); // we guess size
data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
data << uint8(type == DUEL_WON ? 0 : 1); // 0 = just won; 1 = fled
data << duel->opponent->GetName();
data << opponent->GetName();
data << GetName();
SendMessageToSet(&data, true);
}
sScriptMgr->OnPlayerDuelEnd(duel->opponent, this, type);
sScriptMgr->OnPlayerDuelEnd(opponent, this, type);
switch (type)
{
case DUEL_FLED:
// if initiator and opponent are on the same team
// or initiator and opponent are not PvP enabled, forcibly stop attacking
if (duel->initiator->GetTeamId() == duel->opponent->GetTeamId())
if (GetTeamId() == opponent->GetTeamId())
{
duel->initiator->AttackStop();
duel->opponent->AttackStop();
AttackStop();
opponent->AttackStop();
}
else
{
if (!duel->initiator->IsPvP())
duel->initiator->AttackStop();
if (!duel->opponent->IsPvP())
duel->opponent->AttackStop();
if (!IsPvP())
{
AttackStop();
}
if (!opponent->IsPvP())
{
opponent->AttackStop();
}
}
break;
case DUEL_WON:
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1);
duel->opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
// Credit for quest Death's Challenge
if (getClass() == CLASS_DEATH_KNIGHT && duel->opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE)
duel->opponent->CastSpell(duel->opponent, 52994, true);
if (getClass() == CLASS_DEATH_KNIGHT && opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE)
{
opponent->CastSpell(opponent, 52994, true);
}
// Honor points after duel (the winner) - ImpConfig
if (uint32 amount = sWorld->getIntConfig(CONFIG_HONOR_AFTER_DUEL))
duel->opponent->RewardHonor(nullptr, 1, amount);
{
opponent->RewardHonor(nullptr, 1, amount);
}
break;
default:
@@ -6251,56 +6270,66 @@ void Player::DuelComplete(DuelCompleteType type)
}
// Victory emote spell
if (type != DUEL_INTERRUPTED && duel->opponent)
duel->opponent->CastSpell(duel->opponent, 52852, true);
if (type != DUEL_INTERRUPTED)
{
opponent->CastSpell(opponent, 52852, true);
}
//Remove Duel Flag object
GameObject* obj = GetMap()->GetGameObject(GetGuidValue(PLAYER_DUEL_ARBITER));
if (obj)
duel->initiator->RemoveGameObject(obj, true);
{
duel->Initiator->RemoveGameObject(obj, true);
}
/* remove auras */
AuraApplicationMap& itsAuras = duel->opponent->GetAppliedAuras();
AuraApplicationMap &itsAuras = opponent->GetAppliedAuras();
for (AuraApplicationMap::iterator i = itsAuras.begin(); i != itsAuras.end();)
{
Aura const* aura = i->second->GetBase();
if (!i->second->IsPositive() && aura->GetCasterGUID() == GetGUID() && aura->GetApplyTime() >= duel->startTime)
duel->opponent->RemoveAura(i);
if (!i->second->IsPositive() && aura->GetCasterGUID() == GetGUID() && aura->GetApplyTime() >= duel->StartTime)
{
opponent->RemoveAura(i);
}
else
{
++i;
}
}
AuraApplicationMap& myAuras = GetAppliedAuras();
AuraApplicationMap &myAuras = GetAppliedAuras();
for (AuraApplicationMap::iterator i = myAuras.begin(); i != myAuras.end();)
{
Aura const* aura = i->second->GetBase();
if (!i->second->IsPositive() && aura->GetCasterGUID() == duel->opponent->GetGUID() && aura->GetApplyTime() >= duel->startTime)
if (!i->second->IsPositive() && aura->GetCasterGUID() == opponent->GetGUID() && aura->GetApplyTime() >= duel->StartTime)
RemoveAura(i);
else
++i;
}
// cleanup combo points
if (GetComboTarget() == duel->opponent->GetGUID())
if (GetComboTarget() == duel->Opponent->GetGUID())
ClearComboPoints();
else if (GetComboTarget() == duel->opponent->GetPetGUID())
else if (GetComboTarget() == duel->Opponent->GetPetGUID())
ClearComboPoints();
if (duel->opponent->GetComboTarget() == GetGUID())
duel->opponent->ClearComboPoints();
else if (duel->opponent->GetComboTarget() == GetPetGUID())
duel->opponent->ClearComboPoints();
if (duel->Opponent->GetComboTarget() == GetGUID())
{
duel->Opponent->ClearComboPoints();
}
else if (duel->Opponent->GetComboTarget() == GetPetGUID())
{
duel->Opponent->ClearComboPoints();
}
//cleanups
SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
SetUInt32Value(PLAYER_DUEL_TEAM, 0);
duel->opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
delete duel->opponent->duel;
duel->opponent->duel = nullptr;
delete duel;
duel = nullptr;
opponent->duel.reset(nullptr);
duel.reset(nullptr);
}
//---------------------------------------------------------//
@@ -10668,9 +10697,18 @@ bool Player::IsAlwaysDetectableFor(WorldObject const* seer) const
if (Unit::IsAlwaysDetectableFor(seer))
return true;
if (duel && duel->State != DUEL_STATE_CHALLENGED && duel->Opponent == seer)
{
return false;
}
if (const Player* seerPlayer = seer->ToPlayer())
{
if (IsGroupVisibleFor(seerPlayer))
return !(seerPlayer->duel && seerPlayer->duel->startTime != 0 && seerPlayer->duel->opponent == this);
{
return true;
}
}
return false;
}

View File

@@ -357,16 +357,24 @@ struct PvPInfo
time_t FFAPvPEndTimer{0}; ///> Time when player unflags himself for FFA PvP (flag removed after 30 sec)
};
enum DuelState
{
DUEL_STATE_CHALLENGED,
DUEL_STATE_COUNTDOWN,
DUEL_STATE_IN_PROGRESS,
DUEL_STATE_COMPLETED
};
struct DuelInfo
{
DuelInfo() {}
DuelInfo(Player* opponent, Player* initiator, bool isMounted) : Opponent(opponent), Initiator(initiator), IsMounted(isMounted) {}
Player* initiator{nullptr};
Player* opponent{nullptr};
time_t startTimer{0};
time_t startTime{0};
time_t outOfBound{0};
bool isMounted{false};
Player* const Opponent;
Player* const Initiator;
bool const IsMounted;
DuelState State = DUEL_STATE_CHALLENGED;
time_t StartTime = 0;
time_t OutOfBoundsTime = 0;
};
struct Areas
@@ -1814,7 +1822,7 @@ public:
}
/** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/
DuelInfo* duel;
std::unique_ptr<DuelInfo> duel;
void UpdateDuelFlag(time_t currTime);
void CheckDuelDistance(time_t currTime);
void DuelComplete(DuelCompleteType type);

View File

@@ -403,18 +403,16 @@ void Player::UpdateFFAPvPFlag(time_t currTime)
void Player::UpdateDuelFlag(time_t currTime)
{
if (!duel || duel->startTimer == 0 || currTime < duel->startTimer + 3)
return;
if (duel && duel->State == DUEL_STATE_COUNTDOWN && duel->StartTime <= currTime)
{
sScriptMgr->OnPlayerDuelStart(this, duel->Opponent);
sScriptMgr->OnPlayerDuelStart(this, duel->opponent);
SetUInt32Value(PLAYER_DUEL_TEAM, 1);
duel->Opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
SetUInt32Value(PLAYER_DUEL_TEAM, 1);
duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
duel->startTimer = 0;
duel->startTime = currTime;
duel->opponent->duel->startTimer = 0;
duel->opponent->duel->startTime = currTime;
duel->State = DUEL_STATE_IN_PROGRESS;
duel->Opponent->duel->State = DUEL_STATE_IN_PROGRESS;
}
}
/*********************************************************/

View File

@@ -921,7 +921,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
return 0;
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
if (victim->ToPlayer()->duel->opponent == attacker || victim->ToPlayer()->duel->opponent->GetGUID() == attacker->GetOwnerGUID())
if (victim->ToPlayer()->duel->Opponent == attacker || victim->ToPlayer()->duel->Opponent->GetGUID() == attacker->GetOwnerGUID())
damage = health - 1;
duel_hasEnded = true;
@@ -930,14 +930,14 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
{
Player* victimRider = victim->GetCharmer()->ToPlayer();
if (victimRider && victimRider->duel && victimRider->duel->isMounted)
if (victimRider && victimRider->duel && victimRider->duel->IsMounted)
{
// xinef: situation not possible earlier, just return silently.
if (!attacker)
return 0;
// prevent kill only if killed in duel and killed by opponent or opponent controlled creature
if (victimRider->duel->opponent == attacker || victimRider->duel->opponent->GetGUID() == attacker->GetCharmerGUID())
if (victimRider->duel->Opponent == attacker || victimRider->duel->Opponent->GetGUID() == attacker->GetCharmerGUID())
damage = health - 1;
duel_wasMounted = true;
@@ -1065,7 +1065,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
else
he->SetHealth(1);
he->duel->opponent->CombatStopWithPets(true);
he->duel->Opponent->CombatStopWithPets(true);
he->CombatStopWithPets(true);
he->CastSpell(he, 7267, true); // beg
@@ -9545,7 +9545,7 @@ ReputationRank Unit::GetReactionTo(Unit const* target) const
return REP_FRIENDLY;
// duel - always hostile to opponent
if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner && selfPlayerOwner->duel->startTime != 0)
if (selfPlayerOwner->duel && selfPlayerOwner->duel->Opponent == targetPlayerOwner && selfPlayerOwner->duel->StartTime != 0)
return REP_HOSTILE;
// same group - checks dependant only on our faction - skip FFA_PVP for example
@@ -12817,7 +12817,7 @@ void Unit::SetInCombatWith(Unit* enemy, uint32 duration)
if (eOwner->GetTypeId() == TYPEID_PLAYER && eOwner->ToPlayer()->duel)
{
Unit const* myOwner = GetCharmerOrOwnerOrSelf();
if (((Player const*)eOwner)->duel->opponent == myOwner)
if (((Player const*)eOwner)->duel->Opponent == myOwner)
{
SetInCombatState(true, enemy, duration);
return;
@@ -12890,7 +12890,7 @@ void Unit::CombatStart(Unit* victim, bool initialAggro)
SetContestedPvP(who->ToPlayer());
Player* me = GetCharmerOrOwnerPlayerOrPlayerItself();
if (me && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !me->duel || me->duel->opponent != who))
if (me && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !me->duel || me->duel->Opponent != who))
{
me->UpdatePvP(true);
me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
@@ -12917,7 +12917,7 @@ void Unit::CombatStartOnCast(Unit* target, bool initialAggro, uint32 duration)
SetContestedPvP(who->ToPlayer());
Player* me = GetCharmerOrOwnerPlayerOrPlayerItself();
if (me && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !me->duel || me->duel->opponent != who))
if (me && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !me->duel || me->duel->Opponent != who))
{
me->UpdatePvP(true);
me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
@@ -13155,7 +13155,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
// check duel - before sanctuary checks
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->Opponent == playerAffectingTarget && playerAffectingAttacker->duel->StartTime != 0)
return true;
// PvP case - can't attack when attacker or target are in sanctuary
@@ -16466,7 +16466,7 @@ void Unit::SetContestedPvP(Player* attackedPlayer, bool lookForNearContestedGuar
{
Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
if (!player || ((attackedPlayer && (attackedPlayer == player || (player->duel && player->duel->opponent == attackedPlayer))) || player->InBattleground()))
if (!player || ((attackedPlayer && (attackedPlayer == player || (player->duel && player->duel->Opponent == attackedPlayer))) || player->InBattleground()))
return;
// check if there any guards that should care about the contested flag on player
@@ -17098,7 +17098,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
// last damage from non duel opponent or opponent controlled creature
if (plrVictim->duel)
{
plrVictim->duel->opponent->CombatStopWithPets(true);
plrVictim->duel->Opponent->CombatStopWithPets(true);
plrVictim->CombatStopWithPets(true);
plrVictim->DuelComplete(DUEL_INTERRUPTED);
}
@@ -18856,7 +18856,7 @@ void Unit::_ExitVehicle(Position const* exitPosition)
Player* player = ToPlayer();
// If player is on mouted duel and exits the mount should immediatly lose the duel
if (player && player->duel && player->duel->isMounted)
if (player && player->duel && player->duel->IsMounted)
player->DuelComplete(DUEL_FLED);
// This should be done before dismiss, because there may be some aura removal

View File

@@ -24,48 +24,48 @@
void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
{
ObjectGuid guid;
Player* player;
Player* plTarget;
recvPacket >> guid;
if (!GetPlayer()->duel) // ignore accept from duel-sender
Player* player = GetPlayer();
if (!player->duel || player == player->duel->Initiator || player->duel->State != DUEL_STATE_CHALLENGED)
return;
player = GetPlayer();
plTarget = player->duel->opponent;
ObjectGuid guid;
recvPacket >> guid;
if (player == player->duel->initiator || !plTarget || player == plTarget || player->duel->startTime != 0 || plTarget->duel->startTime != 0)
Player* target = player->duel->Opponent;
if (target->GetGuidValue(PLAYER_DUEL_ARBITER) != guid)
return;
LOG_DEBUG("network.opcode", "Player 1 is: %s (%s)", player->GetGUID().ToString().c_str(), player->GetName().c_str());
LOG_DEBUG("network.opcode", "Player 2 is: %s (%s)", plTarget->GetGUID().ToString().c_str(), plTarget->GetName().c_str());
LOG_DEBUG("network.opcode", "Player 2 is: %s (%s)", target->GetGUID().ToString().c_str(), target->GetName().c_str());
time_t now = time(nullptr);
player->duel->startTimer = now;
plTarget->duel->startTimer = now;
player->duel->StartTime = now + 3;
target->duel->StartTime = now + 3;
player->duel->State = DUEL_STATE_COUNTDOWN;
target->duel->State = DUEL_STATE_COUNTDOWN;
player->SendDuelCountdown(3000);
plTarget->SendDuelCountdown(3000);
target->SendDuelCountdown(3000);
}
void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket)
{
LOG_DEBUG("network", "WORLD: Received CMSG_DUEL_CANCELLED");
Player* player = GetPlayer();
ObjectGuid guid;
recvPacket >> guid;
// no duel requested
if (!GetPlayer()->duel)
if (!player->duel || player->duel->State == DUEL_STATE_COMPLETED)
return;
// player surrendered in a duel using /forfeit
if (GetPlayer()->duel->startTime != 0)
if (GetPlayer()->duel->State == DUEL_STATE_IN_PROGRESS)
{
GetPlayer()->CombatStopWithPets(true);
if (GetPlayer()->duel->opponent)
GetPlayer()->duel->opponent->CombatStopWithPets(true);
GetPlayer()->duel->Opponent->CombatStopWithPets(true);
GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg
GetPlayer()->DuelComplete(DUEL_WON);

View File

@@ -5631,7 +5631,7 @@ SpellCastResult Spell::CheckCast(bool strict)
{
// Xinef: fix for duels
Player* player = m_caster->ToPlayer();
if (!player || !player->duel || target != player->duel->opponent)
if (!player || !player->duel || target != player->duel->Opponent)
return SPELL_FAILED_NOTHING_TO_DISPEL;
}
}

View File

@@ -4408,21 +4408,9 @@ void Spell::EffectDuel(SpellEffIndex effIndex)
target->GetSession()->SendPacket(&data);
// create duel-info
DuelInfo* duel = new DuelInfo;
duel->initiator = caster;
duel->opponent = target;
duel->startTime = 0;
duel->startTimer = 0;
duel->isMounted = (GetSpellInfo()->Id == 62875); // Mounted Duel
caster->duel = duel;
DuelInfo* duel2 = new DuelInfo;
duel2->initiator = caster;
duel2->opponent = caster;
duel2->startTime = 0;
duel2->startTimer = 0;
duel2->isMounted = (GetSpellInfo()->Id == 62875); // Mounted Duel
target->duel = duel2;
bool isMounted = (GetSpellInfo()->Id == 62875);
caster->duel = std::make_unique<DuelInfo>(target, caster, isMounted);
target->duel = std::make_unique<DuelInfo>(caster, caster, isMounted);
caster->SetGuidValue(PLAYER_DUEL_ARBITER, pGameObj->GetGUID());
target->SetGuidValue(PLAYER_DUEL_ARBITER, pGameObj->GetGUID());

View File

@@ -221,7 +221,7 @@ public:
areaid == AREA_RING_OF_HORDE_VALIANTS ||
areaid == AREA_RING_OF_CHAMPIONS;
return checkArea && player->duel && player->duel->isMounted;
return checkArea && player->duel && player->duel->IsMounted;
}
};