diff --git a/changelog.txt b/changelog.txt index 29747ba45..3ab8093cc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 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 - 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 diff --git a/common/ruletypes.h b/common/ruletypes.h index c5fc1b9ce..aac46003d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -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, TradeskillUpResearch, 1) // Research 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_INT(Character, IksarCommonTongue, 95) // 95 By default (live-like?) RULE_INT(Character, OgreCommonTongue, 95) // 95 By default (live-like?) diff --git a/zone/client.cpp b/zone/client.cpp index 2df13f25b..73221e279 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -158,7 +158,8 @@ Client::Client(EQStreamInterface* ieqs) m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), last_region_type(RegionTypeUnsupported), 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++) ClientFilters[cf] = FilterShow; @@ -8745,8 +8746,8 @@ void Client::SendHPUpdateMarquee(){ return; /* Health Update Marquee Display: Custom*/ - int8 health_percentage = (int8)(this->cur_hp * 100 / this->max_hp); - if (health_percentage == 100) + uint8 health_percentage = (uint8)(this->cur_hp * 100 / this->max_hp); + if (health_percentage >= 100) return; std::string health_update_notification = StringFormat("Health: %u%%", health_percentage); diff --git a/zone/client.h b/zone/client.h index 50f36ba72..879e6622a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1485,6 +1485,7 @@ private: Timer helm_toggle_timer; Timer aggro_meter_timer; Timer npc_close_scan_timer; + Timer hp_self_update_throttle_timer; glm::vec3 m_Proximity; diff --git a/zone/mob.cpp b/zone/mob.cpp index e795cf040..dfc7a38bd 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -125,7 +125,7 @@ Mob::Mob(const char* in_name, last_z = 0; - last_hp = 100; + AI_Init(); SetMoving(false); @@ -180,6 +180,8 @@ Mob::Mob(const char* in_name, fearspeed = ((float)base_fearspeed) * 0.025f; } + last_hp_percent = 0; + last_hp = 0; current_speed = base_runspeed; @@ -1303,123 +1305,136 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) // sends hp update of this mob to people who might care 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(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); - if (current_hp == last_hp) { + hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.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(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"); ResetHPUpdateTimer(); + return; } else { + + if (IsClient() && RuleB(Character, MarqueeHPUpdates)) + this->CastToClient()->SendHPUpdateMarquee(); + 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; Group *group = nullptr; - // destructor will free the pBuffer 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); + + /* Update those who have us on x-target */ entity_list.QueueClientsByXTarget(this, &hp_app, false); + + /* Update groups using Group LAA health name tag counter */ entity_list.QueueToGroupsForNPCHealthAA(this, &hp_app); - // send to group - if(IsGrouped()) - { + /* Update group */ + if(IsGrouped()) { group = entity_list.GetGroupByMob(this); if(group) //not sure why this might be null, but it happens group->SendHPPacketsFrom(this); } + /* Update Raid */ if(IsClient()){ - Raid *r = entity_list.GetRaidByClient(CastToClient()); - if(r){ - r->SendHPPacketsFrom(this); - } + Raid *raid = entity_list.GetRaidByClient(CastToClient()); + if (raid) + raid->SendHPPacketsFrom(this); } - // send to master - if(GetOwner() && GetOwner()->IsClient()) - { + /* Pet - Update master - group and raid if exists */ + if(GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->QueuePacket(&hp_app, false); group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); + if(group) group->SendHPPacketsFrom(this); - Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - if(r) - r->SendHPPacketsFrom(this); + + Raid *raid = entity_list.GetRaidByClient(GetOwner()->CastToClient()); + if(raid) + raid->SendHPPacketsFrom(this); } - // send to pet - if(GetPet() && GetPet()->IsClient()) - { + /* Send to pet */ + if(GetPet() && GetPet()->IsClient()) { GetPet()->CastToClient()->QueuePacket(&hp_app, false); } - // Update the damage state of destructible objects - if(IsNPC() && IsDestructibleObject()) - { - if (GetHPRatio() > 74) - { - if (GetAppearance() != eaStanding) - { - SendAppearancePacket(AT_DamageState, eaStanding); - _appearance = eaStanding; + /* Destructible objects */ + if (IsNPC() && IsDestructibleObject()) { + if (GetHPRatio() > 74) { + if (GetAppearance() != eaStanding) { + SendAppearancePacket(AT_DamageState, eaStanding); + _appearance = eaStanding; } } - else if (GetHPRatio() > 49) - { - if (GetAppearance() != eaSitting) - { + else if (GetHPRatio() > 49) { + if (GetAppearance() != eaSitting) { SendAppearancePacket(AT_DamageState, eaSitting); _appearance = eaSitting; } } - else if (GetHPRatio() > 24) - { - if (GetAppearance() != eaCrouching) - { + else if (GetHPRatio() > 24) { + if (GetAppearance() != eaCrouching) { SendAppearancePacket(AT_DamageState, eaCrouching); _appearance = eaCrouching; } } - else if (GetHPRatio() > 0) - { - if (GetAppearance() != eaDead) - { + else if (GetHPRatio() > 0) { + if (GetAppearance() != eaDead) { SendAppearancePacket(AT_DamageState, eaDead); _appearance = eaDead; } } - else if (GetAppearance() != eaLooting) - { + else if (GetAppearance() != eaLooting) { SendAppearancePacket(AT_DamageState, 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 diff --git a/zone/mob.h b/zone/mob.h index 1d692d977..c1a300569 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1389,7 +1389,8 @@ protected: int wandertype; int pausetype; - int8 last_hp; + int8 last_hp_percent; + int32 last_hp; int cur_wp; glm::vec4 m_CurrentWayPoint;