mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-14 15:41:30 +00:00
Fix /camp rest timer exploit
This commit is contained in:
parent
d0956194af
commit
37ff8c830d
@ -129,7 +129,7 @@ void Timer::SetTimer(uint32 set_timer_time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Timer::GetRemainingTime() {
|
uint32 Timer::GetRemainingTime() const {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
if (current_time - start_time > timer_time)
|
if (current_time - start_time > timer_time)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public:
|
|||||||
void Disable();
|
void Disable();
|
||||||
void Start(uint32 set_timer_time=0, bool ChangeResetTimer = true);
|
void Start(uint32 set_timer_time=0, bool ChangeResetTimer = true);
|
||||||
void SetTimer(uint32 set_timer_time=0);
|
void SetTimer(uint32 set_timer_time=0);
|
||||||
uint32 GetRemainingTime();
|
uint32 GetRemainingTime() const;
|
||||||
inline const uint32& GetTimerTime() { return timer_time; }
|
inline const uint32& GetTimerTime() { return timer_time; }
|
||||||
inline const uint32& GetSetAtTrigger() { return set_at_trigger; }
|
inline const uint32& GetSetAtTrigger() { return set_at_trigger; }
|
||||||
void Trigger();
|
void Trigger();
|
||||||
|
|||||||
@ -325,9 +325,6 @@ Client::Client(EQStreamInterface* ieqs)
|
|||||||
initial_respawn_selection = 0;
|
initial_respawn_selection = 0;
|
||||||
alternate_currency_loaded = false;
|
alternate_currency_loaded = false;
|
||||||
|
|
||||||
EngagedRaidTarget = false;
|
|
||||||
SavedRaidRestTimer = 0;
|
|
||||||
|
|
||||||
interrogateinv_flag = false;
|
interrogateinv_flag = false;
|
||||||
|
|
||||||
trapid = 0;
|
trapid = 0;
|
||||||
@ -630,7 +627,7 @@ bool Client::Save(uint8 iCommitNow) {
|
|||||||
/* Total Time Played */
|
/* Total Time Played */
|
||||||
TotalSecondsPlayed += (time(nullptr) - m_pp.lastlogin);
|
TotalSecondsPlayed += (time(nullptr) - m_pp.lastlogin);
|
||||||
m_pp.timePlayedMin = (TotalSecondsPlayed / 60);
|
m_pp.timePlayedMin = (TotalSecondsPlayed / 60);
|
||||||
m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000;
|
m_pp.RestTimer = GetRestTimer();
|
||||||
|
|
||||||
/* Save Mercs */
|
/* Save Mercs */
|
||||||
if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) {
|
if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) {
|
||||||
@ -4594,29 +4591,31 @@ int Client::GetAggroCount() {
|
|||||||
return AggroCount;
|
return AggroCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::IncrementAggroCount() {
|
// we pass in for book keeping if RestRegen is enabled
|
||||||
|
void Client::IncrementAggroCount(bool raid_target)
|
||||||
|
{
|
||||||
// This method is called when a client is added to a mob's hate list. It turns the clients aggro flag on so
|
// This method is called when a client is added to a mob's hate list. It turns the clients aggro flag on so
|
||||||
// rest state regen is stopped, and for SoF, it sends the opcode to show the crossed swords in-combat indicator.
|
// rest state regen is stopped, and for SoF, it sends the opcode to show the crossed swords in-combat indicator.
|
||||||
//
|
|
||||||
//
|
|
||||||
AggroCount++;
|
AggroCount++;
|
||||||
|
|
||||||
if(!RuleB(Character, RestRegenEnabled))
|
if(!RuleB(Character, RestRegenEnabled))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
uint32 newtimer = raid_target ? RuleI(Character, RestRegenRaidTimeToActivate) : RuleI(Character, RestRegenTimeToActivate);
|
||||||
|
|
||||||
|
m_pp.RestTimer = std::max(m_pp.RestTimer, newtimer);
|
||||||
|
|
||||||
// If we already had aggro before this method was called, the combat indicator should already be up for SoF clients,
|
// If we already had aggro before this method was called, the combat indicator should already be up for SoF clients,
|
||||||
// so we don't need to send it again.
|
// so we don't need to send it again.
|
||||||
//
|
//
|
||||||
if(AggroCount > 1)
|
if(AggroCount > 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Pause the rest timer
|
// Pause the rest timer, it's possible the new timer is a non-raid timer we're currently ticking down on a raid timer
|
||||||
if (AggroCount == 1)
|
if (AggroCount == 1)
|
||||||
SavedRaidRestTimer = rest_timer.GetRemainingTime();
|
m_pp.RestTimer = std::max(m_pp.RestTimer, rest_timer.GetRemainingTime() / 1000);
|
||||||
|
|
||||||
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) {
|
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) {
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_RestState, 1);
|
auto outapp = new EQApplicationPacket(OP_RestState, 1);
|
||||||
char *Buffer = (char *)outapp->pBuffer;
|
char *Buffer = (char *)outapp->pBuffer;
|
||||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x01);
|
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x01);
|
||||||
@ -4626,12 +4625,11 @@ void Client::IncrementAggroCount() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::DecrementAggroCount() {
|
void Client::DecrementAggroCount()
|
||||||
|
{
|
||||||
// This should be called when a client is removed from a mob's hate list (it dies or is memblurred).
|
// This should be called when a client is removed from a mob's hate list (it dies or is memblurred).
|
||||||
// It checks whether any other mob is aggro on the player, and if not, starts the rest timer.
|
// It checks whether any other mob is aggro on the player, and if not, starts the rest timer.
|
||||||
// For SoF, the opcode to start the rest state countdown timer in the UI is sent.
|
// For SoF, the opcode to start the rest state countdown timer in the UI is sent.
|
||||||
//
|
|
||||||
|
|
||||||
// If we didn't have aggro before, this method should not have been called.
|
// If we didn't have aggro before, this method should not have been called.
|
||||||
if(!AggroCount)
|
if(!AggroCount)
|
||||||
@ -4643,29 +4641,16 @@ void Client::DecrementAggroCount() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Something else is still aggro on us, can't rest yet.
|
// Something else is still aggro on us, can't rest yet.
|
||||||
if(AggroCount) return;
|
if (AggroCount)
|
||||||
|
return;
|
||||||
|
|
||||||
uint32 time_until_rest;
|
rest_timer.Start(m_pp.RestTimer * 1000);
|
||||||
if (GetEngagedRaidTarget()) {
|
|
||||||
time_until_rest = RuleI(Character, RestRegenRaidTimeToActivate) * 1000;
|
|
||||||
SetEngagedRaidTarget(false);
|
|
||||||
} else {
|
|
||||||
if (SavedRaidRestTimer > (RuleI(Character, RestRegenTimeToActivate) * 1000)) {
|
|
||||||
time_until_rest = SavedRaidRestTimer;
|
|
||||||
SavedRaidRestTimer = 0;
|
|
||||||
} else {
|
|
||||||
time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rest_timer.Start(time_until_rest);
|
|
||||||
|
|
||||||
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) {
|
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) {
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_RestState, 5);
|
auto outapp = new EQApplicationPacket(OP_RestState, 5);
|
||||||
char *Buffer = (char *)outapp->pBuffer;
|
char *Buffer = (char *)outapp->pBuffer;
|
||||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00);
|
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00);
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, (uint32)(time_until_rest / 1000));
|
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, m_pp.RestTimer);
|
||||||
QueuePacket(outapp);
|
QueuePacket(outapp);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1074,7 +1074,7 @@ public:
|
|||||||
void ClearPendingAdventureData();
|
void ClearPendingAdventureData();
|
||||||
|
|
||||||
int GetAggroCount();
|
int GetAggroCount();
|
||||||
void IncrementAggroCount();
|
void IncrementAggroCount(bool raid_target = false);
|
||||||
void DecrementAggroCount();
|
void DecrementAggroCount();
|
||||||
void SendPVPStats();
|
void SendPVPStats();
|
||||||
void SendDisciplineTimers();
|
void SendDisciplineTimers();
|
||||||
@ -1278,9 +1278,6 @@ public:
|
|||||||
int mod_food_value(const EQEmu::ItemData *item, int change);
|
int mod_food_value(const EQEmu::ItemData *item, int change);
|
||||||
int mod_drink_value(const EQEmu::ItemData *item, int change);
|
int mod_drink_value(const EQEmu::ItemData *item, int change);
|
||||||
|
|
||||||
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
|
|
||||||
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
|
|
||||||
|
|
||||||
void ShowNumHits(); // work around function for numhits not showing on buffs
|
void ShowNumHits(); // work around function for numhits not showing on buffs
|
||||||
|
|
||||||
void TripInterrogateInvState() { interrogateinv_flag = true; }
|
void TripInterrogateInvState() { interrogateinv_flag = true; }
|
||||||
@ -1398,6 +1395,8 @@ private:
|
|||||||
void DoManaRegen();
|
void DoManaRegen();
|
||||||
void DoStaminaHungerUpdate();
|
void DoStaminaHungerUpdate();
|
||||||
void CalcRestState();
|
void CalcRestState();
|
||||||
|
// if they have aggro (AggroCount != 0) their timer is saved in m_pp.RestTimer, else we need to get current timer
|
||||||
|
inline uint32 GetRestTimer() const { return AggroCount ? m_pp.RestTimer : rest_timer.GetRemainingTime() / 1000; }
|
||||||
|
|
||||||
uint32 pLastUpdate;
|
uint32 pLastUpdate;
|
||||||
uint32 pLastUpdateWZ;
|
uint32 pLastUpdateWZ;
|
||||||
@ -1565,9 +1564,6 @@ private:
|
|||||||
float AreaManaRegen;
|
float AreaManaRegen;
|
||||||
float AreaEndRegen;
|
float AreaEndRegen;
|
||||||
|
|
||||||
bool EngagedRaidTarget;
|
|
||||||
uint32 SavedRaidRestTimer;
|
|
||||||
|
|
||||||
std::set<uint32> zone_flags;
|
std::set<uint32> zone_flags;
|
||||||
|
|
||||||
ClientTaskState *taskstate;
|
ClientTaskState *taskstate;
|
||||||
|
|||||||
@ -1933,12 +1933,11 @@ void Client::DoEnduranceUpkeep() {
|
|||||||
SetEndurUpkeep(false);
|
SetEndurUpkeep(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::CalcRestState() {
|
void Client::CalcRestState()
|
||||||
|
{
|
||||||
// This method calculates rest state HP and mana regeneration.
|
// This method calculates rest state HP and mana regeneration.
|
||||||
// The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
// The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
||||||
// must be sitting down, and must not have any detrimental spells affecting them.
|
// must be sitting down, and must not have any detrimental spells affecting them.
|
||||||
//
|
|
||||||
if(!RuleB(Character, RestRegenEnabled))
|
if(!RuleB(Character, RestRegenEnabled))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1950,6 +1949,9 @@ void Client::CalcRestState() {
|
|||||||
if(!rest_timer.Check(false))
|
if(!rest_timer.Check(false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// so we don't have aggro, our timer has expired, we do not want this to cause issues
|
||||||
|
m_pp.RestTimer = 0;
|
||||||
|
|
||||||
uint32 buff_count = GetMaxTotalSlots();
|
uint32 buff_count = GetMaxTotalSlots();
|
||||||
for (unsigned int j = 0; j < buff_count; j++) {
|
for (unsigned int j = 0; j < buff_count; j++) {
|
||||||
if(buffs[j].spellid != SPELL_UNKNOWN) {
|
if(buffs[j].spellid != SPELL_UNKNOWN) {
|
||||||
@ -1960,7 +1962,6 @@ void Client::CalcRestState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ooc_regen = true;
|
ooc_regen = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::DoTracking()
|
void Client::DoTracking()
|
||||||
|
|||||||
@ -207,9 +207,7 @@ void HateList::AddEntToHateList(Mob *in_entity, int32 in_hate, int32 in_damage,
|
|||||||
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "1", 0);
|
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "1", 0);
|
||||||
|
|
||||||
if (in_entity->IsClient()) {
|
if (in_entity->IsClient()) {
|
||||||
if (hate_owner->CastToNPC()->IsRaidTarget())
|
in_entity->CastToClient()->IncrementAggroCount(hate_owner->CastToNPC()->IsRaidTarget());
|
||||||
in_entity->CastToClient()->SetEngagedRaidTarget(true);
|
|
||||||
in_entity->CastToClient()->IncrementAggroCount();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user