Fix HP update issues, rework logic for more accurate and responsive HP updates

This commit is contained in:
Akkadius 2017-07-09 17:35:08 -05:00
parent 127f51e758
commit ccdeb4d385
6 changed files with 87 additions and 69 deletions

View File

@ -1,6 +1,7 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 7/9/2017 == == 7/9/2017 ==
Akkadius: Fix HP update issues, rework logic for more accurate HP updates
Akkadius: Massive reductions in unnecessary network traffic especially during high spam combat fights Akkadius: Massive reductions in unnecessary network traffic especially during high spam combat fights
- HP Updates now only send to others when HP percentage changes (0-100%) - HP Updates now only send to others when HP percentage changes (0-100%)
- HP Updates were sending excessively even during idle zones when HP wasn't changing at all - HP Updates were sending excessively even during idle zones when HP wasn't changing at all

View File

@ -134,7 +134,6 @@ RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjus
RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster. RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster. RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster. RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster.
RULE_BOOL(Character, SpamHPUpdates, false) // if your server has stupid amounts of HP that causes client display issues, turn this on!
RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100% RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100%
RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?) RULE_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?)
RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?) RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?)

View File

@ -158,7 +158,8 @@ Client::Client(EQStreamInterface* ieqs)
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported), last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false), m_dirtyautohaters(false),
npc_close_scan_timer(6000) npc_close_scan_timer(6000),
hp_self_update_throttle_timer(500)
{ {
for(int cf=0; cf < _FilterCount; cf++) for(int cf=0; cf < _FilterCount; cf++)
ClientFilters[cf] = FilterShow; ClientFilters[cf] = FilterShow;
@ -8745,8 +8746,8 @@ void Client::SendHPUpdateMarquee(){
return; return;
/* Health Update Marquee Display: Custom*/ /* Health Update Marquee Display: Custom*/
int8 health_percentage = (int8)(this->cur_hp * 100 / this->max_hp); uint8 health_percentage = (uint8)(this->cur_hp * 100 / this->max_hp);
if (health_percentage == 100) if (health_percentage >= 100)
return; return;
std::string health_update_notification = StringFormat("Health: %u%%", health_percentage); std::string health_update_notification = StringFormat("Health: %u%%", health_percentage);

View File

@ -1485,6 +1485,7 @@ private:
Timer helm_toggle_timer; Timer helm_toggle_timer;
Timer aggro_meter_timer; Timer aggro_meter_timer;
Timer npc_close_scan_timer; Timer npc_close_scan_timer;
Timer hp_self_update_throttle_timer;
glm::vec3 m_Proximity; glm::vec3 m_Proximity;

View File

@ -125,7 +125,7 @@ Mob::Mob(const char* in_name,
last_z = 0; last_z = 0;
last_hp = 100;
AI_Init(); AI_Init();
SetMoving(false); SetMoving(false);
@ -180,6 +180,8 @@ Mob::Mob(const char* in_name,
fearspeed = ((float)base_fearspeed) * 0.025f; fearspeed = ((float)base_fearspeed) * 0.025f;
} }
last_hp_percent = 0;
last_hp = 0;
current_speed = base_runspeed; current_speed = base_runspeed;
@ -1303,123 +1305,136 @@ void Mob::CreateHPPacket(EQApplicationPacket* app)
// sends hp update of this mob to people who might care // sends hp update of this mob to people who might care
void Mob::SendHPUpdate(bool skip_self) void Mob::SendHPUpdate(bool skip_self)
{ {
/* If our HP is different from last HP update call - let's update ourself */
if (IsClient() && cur_hp != last_hp) {
/* This is to prevent excessive packet sending under trains/fast combat */
if (this->CastToClient()->hp_self_update_throttle_timer.Check()) {
Log(Logs::General, Logs::HP_Update,
"Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s",
this->GetCleanName(),
cur_hp,
last_hp,
(skip_self ? "true" : "false")
);
int8 current_hp = (max_hp == 0 ? 0 : static_cast<int>(cur_hp * 100 / max_hp)); if (!skip_self || this->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::UF) {
auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
SpawnHPUpdate_Struct* hp_packet_client = (SpawnHPUpdate_Struct*)client_packet->pBuffer;
Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp, last_hp); hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP;
if (current_hp == last_hp) { hp_packet_client->spawn_id = GetID();
hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
CastToClient()->QueuePacket(client_packet);
safe_delete(client_packet);
ResetHPUpdateTimer();
}
}
}
/* Used to check if HP has changed to update self next round */
last_hp = cur_hp;
int8 current_hp_percent = (max_hp == 0 ? 0 : static_cast<int>(cur_hp * 100 / max_hp));
Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp_percent, last_hp_percent);
if (current_hp_percent == last_hp_percent) {
Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update"); Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update");
ResetHPUpdateTimer(); ResetHPUpdateTimer();
return; return;
} }
else { else {
if (IsClient() && RuleB(Character, MarqueeHPUpdates))
this->CastToClient()->SendHPUpdateMarquee();
Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update"); Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update");
last_hp = current_hp;
last_hp_percent = current_hp_percent;
} }
EQApplicationPacket hp_app; EQApplicationPacket hp_app;
Group *group = nullptr; Group *group = nullptr;
// destructor will free the pBuffer
CreateHPPacket(&hp_app); CreateHPPacket(&hp_app);
// send to people who have us targeted /* Update those who have use targeted */
entity_list.QueueClientsByTarget(this, &hp_app, false, 0, false, true, EQEmu::versions::bit_AllClients); entity_list.QueueClientsByTarget(this, &hp_app, false, 0, false, true, EQEmu::versions::bit_AllClients);
/* Update those who have us on x-target */
entity_list.QueueClientsByXTarget(this, &hp_app, false); entity_list.QueueClientsByXTarget(this, &hp_app, false);
/* Update groups using Group LAA health name tag counter */
entity_list.QueueToGroupsForNPCHealthAA(this, &hp_app); entity_list.QueueToGroupsForNPCHealthAA(this, &hp_app);
// send to group /* Update group */
if(IsGrouped()) if(IsGrouped()) {
{
group = entity_list.GetGroupByMob(this); group = entity_list.GetGroupByMob(this);
if(group) //not sure why this might be null, but it happens if(group) //not sure why this might be null, but it happens
group->SendHPPacketsFrom(this); group->SendHPPacketsFrom(this);
} }
/* Update Raid */
if(IsClient()){ if(IsClient()){
Raid *r = entity_list.GetRaidByClient(CastToClient()); Raid *raid = entity_list.GetRaidByClient(CastToClient());
if(r){ if (raid)
r->SendHPPacketsFrom(this); raid->SendHPPacketsFrom(this);
}
} }
// send to master /* Pet - Update master - group and raid if exists */
if(GetOwner() && GetOwner()->IsClient()) if(GetOwner() && GetOwner()->IsClient()) {
{
GetOwner()->CastToClient()->QueuePacket(&hp_app, false); GetOwner()->CastToClient()->QueuePacket(&hp_app, false);
group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); group = entity_list.GetGroupByClient(GetOwner()->CastToClient());
if(group) if(group)
group->SendHPPacketsFrom(this); group->SendHPPacketsFrom(this);
Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient());
if(r) Raid *raid = entity_list.GetRaidByClient(GetOwner()->CastToClient());
r->SendHPPacketsFrom(this); if(raid)
raid->SendHPPacketsFrom(this);
} }
// send to pet /* Send to pet */
if(GetPet() && GetPet()->IsClient()) if(GetPet() && GetPet()->IsClient()) {
{
GetPet()->CastToClient()->QueuePacket(&hp_app, false); GetPet()->CastToClient()->QueuePacket(&hp_app, false);
} }
// Update the damage state of destructible objects /* Destructible objects */
if(IsNPC() && IsDestructibleObject()) if (IsNPC() && IsDestructibleObject()) {
{ if (GetHPRatio() > 74) {
if (GetHPRatio() > 74) if (GetAppearance() != eaStanding) {
{ SendAppearancePacket(AT_DamageState, eaStanding);
if (GetAppearance() != eaStanding) _appearance = eaStanding;
{
SendAppearancePacket(AT_DamageState, eaStanding);
_appearance = eaStanding;
} }
} }
else if (GetHPRatio() > 49) else if (GetHPRatio() > 49) {
{ if (GetAppearance() != eaSitting) {
if (GetAppearance() != eaSitting)
{
SendAppearancePacket(AT_DamageState, eaSitting); SendAppearancePacket(AT_DamageState, eaSitting);
_appearance = eaSitting; _appearance = eaSitting;
} }
} }
else if (GetHPRatio() > 24) else if (GetHPRatio() > 24) {
{ if (GetAppearance() != eaCrouching) {
if (GetAppearance() != eaCrouching)
{
SendAppearancePacket(AT_DamageState, eaCrouching); SendAppearancePacket(AT_DamageState, eaCrouching);
_appearance = eaCrouching; _appearance = eaCrouching;
} }
} }
else if (GetHPRatio() > 0) else if (GetHPRatio() > 0) {
{ if (GetAppearance() != eaDead) {
if (GetAppearance() != eaDead)
{
SendAppearancePacket(AT_DamageState, eaDead); SendAppearancePacket(AT_DamageState, eaDead);
_appearance = eaDead; _appearance = eaDead;
} }
} }
else if (GetAppearance() != eaLooting) else if (GetAppearance() != eaLooting) {
{
SendAppearancePacket(AT_DamageState, eaLooting); SendAppearancePacket(AT_DamageState, eaLooting);
_appearance = eaLooting; _appearance = eaLooting;
} }
} }
bool dospam = RuleB(Character, SpamHPUpdates);
// send to self - we need the actual hps here
if(IsClient() && (!skip_self || dospam)) {
if (RuleB(Character, MarqueeHPUpdates))
this->CastToClient()->SendHPUpdateMarquee();
auto hp_app2 = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer;
ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP;
ds->spawn_id = GetID();
ds->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP;
CastToClient()->QueuePacket(hp_app2);
safe_delete(hp_app2);
}
if (!dospam)
ResetHPUpdateTimer(); // delay the timer
} }
// this one just warps the mob to the current location // this one just warps the mob to the current location

View File

@ -1389,7 +1389,8 @@ protected:
int wandertype; int wandertype;
int pausetype; int pausetype;
int8 last_hp; int8 last_hp_percent;
int32 last_hp;
int cur_wp; int cur_wp;
glm::vec4 m_CurrentWayPoint; glm::vec4 m_CurrentWayPoint;