From ccdeb4d385218bf53ba34be6fefdacb961088cf8 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 9 Jul 2017 17:35:08 -0500 Subject: [PATCH 01/10] Fix HP update issues, rework logic for more accurate and responsive HP updates --- changelog.txt | 1 + common/ruletypes.h | 1 - zone/client.cpp | 7 ++- zone/client.h | 1 + zone/mob.cpp | 143 +++++++++++++++++++++++++-------------------- zone/mob.h | 3 +- 6 files changed, 87 insertions(+), 69 deletions(-) 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; From c6c6d00badbbf089d02c92a75ddb6b7c9edc2dcb Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 10:42:07 -0500 Subject: [PATCH 02/10] #path process file_name.path will actually push to the maps/ directory by default now --- zone/pathing.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 417ee3af2..25d996eac 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -1454,7 +1454,10 @@ void PathManager::NodeInfo(Client *c) void PathManager::DumpPath(std::string filename) { std::ofstream o_file; - o_file.open(filename.c_str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out); + + std::string file_to_write = StringFormat("%s%s", Config->MapDir.c_str(), filename.c_str()); + + o_file.open(file_to_write.c_str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out); o_file.write("EQEMUPATH", 9); o_file.write((const char*)&Head, sizeof(Head)); o_file.write((const char*)PathNodes, (sizeof(PathNode)*Head.PathNodeCount)); From 122e71f4a35e7f5efdd442db8201907c5cddf0d1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 20:40:32 -0500 Subject: [PATCH 03/10] Fix HP/Mana/Endurance updates for group/raids when entering-leaving zone and when initially being added to group/raid --- zone/client_packet.cpp | 9 +++++++ zone/groups.cpp | 22 +++++++++++------ zone/raids.cpp | 55 ++++++++++++++++++++++++------------------ 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2ca3a1ed4..daf2e5f97 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -583,8 +583,17 @@ void Client::CompleteConnect() if (raid->IsLocked()) raid->SendRaidLockTo(this); + + raid->SendHPPacketsTo(this); } } + else { + Group *group_update = nullptr; + group_update = this->GetGroup(); + if (group_update) + group_update->SendHPPacketsTo(this); + } + //bulk raid send in here eventually diff --git a/zone/groups.cpp b/zone/groups.cpp index 6216de4ac..28b4abd6a 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -338,6 +338,13 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true); } } + + Group* group = newmember->CastToClient()->GetGroup(); + if (group) { + group->SendHPPacketsTo(newmember); + group->SendHPPacketsFrom(newmember); + } + } else { @@ -389,26 +396,25 @@ void Group::QueuePacket(const EQApplicationPacket *app, bool ack_req) // first joins a group, but otherwise there shouldn't be a need to call it void Group::SendHPPacketsTo(Mob *member) { - if(member && member->IsClient()) - { + if(member && member->IsClient()) { EQApplicationPacket hpapp; EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if(members[i] && members[i] != member) - { + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(members[i] && members[i] != member) { members[i]->CreateHPPacket(&hpapp); member->CastToClient()->QueuePacket(&hpapp, false); safe_delete_array(hpapp.pBuffer); hpapp.size = 0; - if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) - { + + if (member->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; mmus->spawn_id = members[i]->GetID(); mmus->mana = members[i]->GetManaPercent(); member->CastToClient()->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; outapp.SetOpcode(OP_MobEnduranceUpdate); meus->endurance = members[i]->GetEndurancePercent(); diff --git a/zone/raids.cpp b/zone/raids.cpp index c9d4ee6c5..d32ad4def 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -145,6 +145,13 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo } } + Group *group_update = nullptr; + group_update = c->GetGroup(); + if (group_update) { + group_update->SendHPPacketsTo(c); + group_update->SendHPPacketsFrom(c); + } + auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -1539,35 +1546,37 @@ void Raid::MemberZoned(Client *c) group_mentor[gid].mentoree = nullptr; } -void Raid::SendHPPacketsTo(Client *c) +void Raid::SendHPPacketsTo(Client *client) { - if(!c) + if(!client) return; - uint32 gid = this->GetGroup(c); - EQApplicationPacket hpapp; + uint32 group_id = this->GetGroup(client); + + EQApplicationPacket hp_packet; EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(members[x].member) - { - if((members[x].member != c) && (members[x].GroupNumber == gid)) - { - members[x].member->CreateHPPacket(&hpapp); - c->QueuePacket(&hpapp, false); - safe_delete_array(hpapp.pBuffer); - hpapp.size = 0; - if (c->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) - { + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) { + if(members[x].member) { + if((members[x].member != client) && (members[x].GroupNumber == group_id)) { + + members[x].member->CreateHPPacket(&hp_packet); + client->QueuePacket(&hp_packet, false); + safe_delete_array(hp_packet.pBuffer); + + hp_packet.size = 0; + if (client->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { + outapp.SetOpcode(OP_MobManaUpdate); - MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; - mmus->spawn_id = members[x].member->GetID(); - mmus->mana = members[x].member->GetManaPercent(); - c->QueuePacket(&outapp, false); + MobManaUpdate_Struct *mana_update = (MobManaUpdate_Struct *)outapp.pBuffer; + mana_update->spawn_id = members[x].member->GetID(); + mana_update->mana = members[x].member->GetManaPercent(); + client->QueuePacket(&outapp, false); + outapp.SetOpcode(OP_MobEnduranceUpdate); - MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; - meus->endurance = members[x].member->GetEndurancePercent(); - c->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *endurance_update = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + endurance_update->endurance = members[x].member->GetEndurancePercent(); + client->QueuePacket(&outapp, false); } } } From 62e4169e504d47d285ababeef35d726e1c06e9f2 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 20:41:56 -0500 Subject: [PATCH 04/10] More animation packet reductions, DoAnim is called in many other places in combat than just AttackAnimation --- zone/attack.cpp | 7 ++++--- zone/mob.cpp | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index ddd8c04ce..d04bbf0d7 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -55,6 +55,9 @@ extern Zone* zone; EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse) { + if (!attack_anim_timer.Check()) + return skillinuse; + // Determine animation int type = 0; if (weapon && weapon->IsClassCommon()) { @@ -137,9 +140,7 @@ EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstanc if (Hand == EQEmu::inventory::slotSecondary) // DW anim type = animDualWield; - if (attack_anim_timer.Check()) { - DoAnim(type, 0, false); - } + DoAnim(type, 0, false); return skillinuse; } diff --git a/zone/mob.cpp b/zone/mob.cpp index dfc7a38bd..830ac9a61 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1568,6 +1568,9 @@ void Mob::ShowStats(Client* client) } void Mob::DoAnim(const int animnum, int type, bool ackreq, eqFilterType filter) { + if (!attack_anim_timer.Check()) + return; + auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct)); Animation_Struct* anim = (Animation_Struct*)outapp->pBuffer; anim->spawnid = GetID(); From a41c690a6245803d4602d07847985483b293b7f3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 22:27:03 -0500 Subject: [PATCH 05/10] Another animation adjustment --- zone/attack.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index d04bbf0d7..ee1384b44 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -55,9 +55,6 @@ extern Zone* zone; EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse) { - if (!attack_anim_timer.Check()) - return skillinuse; - // Determine animation int type = 0; if (weapon && weapon->IsClassCommon()) { From dceb79ad696adac4fadd14839a879fe1f647e409 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 22:57:58 -0500 Subject: [PATCH 06/10] Only send mana/endurance updates to self when they actually change --- zone/client.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 73221e279..03e113ed0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1833,13 +1833,13 @@ void Client::SendManaUpdatePacket() { if (!Connected()) return; - if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { - SendManaUpdate(); - SendEnduranceUpdate(); - } - if (last_reported_mana != cur_mana || last_reported_endur != cur_end) { + if (ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { + SendManaUpdate(); + SendEnduranceUpdate(); + } + auto outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; manachange->new_mana = cur_mana; From ec00daa5be0ffbfc61623abc5bc1235f95f7da4d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 10 Jul 2017 23:03:40 -0500 Subject: [PATCH 07/10] Mob position updates now completely only send to 600 units range defined by Range:MobPositionUpdates Client updates nearby clients more often because they will disappear after 10 seconds without a position update to the client This results in a massive reduction in unnecessary traffic as we only update clients of their relevance around them This also resembles live-like packet sending behavior of positions --- common/ruletypes.h | 1 - zone/client_packet.cpp | 8 +++---- zone/client_process.cpp | 32 +++++++++++++++++-------- zone/entity.cpp | 45 ++++++++++++++++++++-------------- zone/entity.h | 2 +- zone/mob.cpp | 53 +++++++++++++++-------------------------- zone/mob.h | 1 - 7 files changed, 73 insertions(+), 69 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index aac46003d..397f1a832 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -234,7 +234,6 @@ RULE_BOOL(World, StartZoneSameAsBindOnCreation, true) //Should the start zone AL RULE_CATEGORY_END() RULE_CATEGORY(Zone) -RULE_INT(Zone, NPCPositonUpdateTicCount, 32) //ms between intervals of sending a position update to the entire zone. RULE_INT(Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection RULE_INT(Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone RULE_BOOL(Zone, EnableShadowrest, 1) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index daf2e5f97..e4891b999 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -588,10 +588,10 @@ void Client::CompleteConnect() } } else { - Group *group_update = nullptr; - group_update = this->GetGroup(); - if (group_update) - group_update->SendHPPacketsTo(this); + Group *group = nullptr; + group = this->GetGroup(); + if (group) + group->SendHPPacketsTo(this); } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 583ad627b..a5ced5d1a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -256,17 +256,29 @@ bool Client::Process() { close_npcs.clear(); - auto &npc_list = entity_list.GetNPCList(); + auto &mob_list = entity_list.GetMobList(); + float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); + float client_update_range = (RuleI(Range, MobPositionUpdates) * RuleI(Range, MobPositionUpdates)); - float scan_range = RuleI(Range, ClientNPCScan); - for (auto itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = itr->second; - float distance = DistanceNoZ(m_Position, npc->GetPosition()); - if(distance <= scan_range) { - close_npcs.insert(std::pair(npc, distance)); + for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { + Mob* mob = itr->second; + float distance = DistanceSquared(m_Position, mob->GetPosition()); + if (mob->IsNPC()) { + if (distance <= scan_range) { + close_npcs.insert(std::pair(mob->CastToNPC(), distance)); + } + else if (mob->GetAggroRange() > scan_range) { + close_npcs.insert(std::pair(mob->CastToNPC(), distance)); + } } - else if (npc->GetAggroRange() > scan_range) { - close_npcs.insert(std::pair(npc, distance)); + + /* Clients need to be kept up to date for position updates more often otherwise they disappear */ + if (mob->IsClient() && this != mob && distance <= client_update_range) { + auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spawn_update = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + mob->MakeSpawnUpdateNoDelta(spawn_update); + this->FastQueuePacket(&app, false); + safe_delete(app); } } } @@ -455,7 +467,7 @@ bool Client::Process() { // Send a position packet every 8 seconds - if not done, other clients // see this char disappear after 10-12 seconds of inactivity if (position_timer_counter >= 36) { // Approx. 4 ticks per second - entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, GetTarget(), true); + entity_list.SendPositionUpdates(this, pLastUpdateWZ, RuleI(Range, MobPositionUpdates), GetTarget(), true); pLastUpdate = Timer::GetCurrentTime(); pLastUpdateWZ = pLastUpdate; position_timer_counter = 0; diff --git a/zone/entity.cpp b/zone/entity.cpp index 35d67a5d3..eee89ce0f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2634,10 +2634,9 @@ void EntityList::RemoveDebuffs(Mob *caster) // Currently, a new packet is sent per entity. // @todo: Come back and use FLAG_COMBINED to pack // all updates into one packet. -void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, - float range, Entity *alwayssend, bool iSendEvenIfNotChanged) +void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, float update_range, Entity *always_send, bool iSendEvenIfNotChanged) { - range = range * range; + update_range = (update_range * update_range); EQApplicationPacket *outapp = 0; PlayerPositionUpdateServer_Struct *ppu = 0; @@ -2645,27 +2644,37 @@ void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, auto it = mob_list.begin(); while (it != mob_list.end()) { - if (outapp == 0) { - outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; - } + mob = it->second; - if (mob && !mob->IsCorpse() && (it->second != client) + + if ( + mob && !mob->IsCorpse() + && (it->second != client) && (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate)) - && (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client))) { + && (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client)) + ) { + if ( + update_range == 0 + || (it->second == always_send) + || mob->IsClient() + || (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= update_range) + ) { + if (mob && mob->IsClient() && mob->GetID() > 0) { + client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); - //bool Grouped = client->HasGroup() && mob->IsClient() && (client->GetGroup() == mob->CastToClient()->GetGroup()); + if (outapp == 0) { + outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; + } - //if (range == 0 || (iterator.GetData() == alwayssend) || Grouped || (mob->DistNoRootNoZ(*client) <= range)) { - if (range == 0 || (it->second == alwayssend) || mob->IsClient() || (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= range)) { - mob->MakeSpawnUpdate(ppu); - } - if(mob && mob->IsClient() && mob->GetID()>0) { - client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + mob->MakeSpawnUpdate(ppu); + + safe_delete(outapp); + outapp = 0; + } } } - safe_delete(outapp); - outapp = 0; + ++it; } diff --git a/zone/entity.h b/zone/entity.h index 7752e97b1..16249547b 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -370,7 +370,7 @@ public: Mob* FindDefenseNPC(uint32 npcid); void OpenDoorsNear(NPC* opener); void UpdateWho(bool iSendFullUpdate = false); - void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float range = 0, Entity* alwayssend = 0, bool iSendEvenIfNotChanged = false); + void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float update_range = 0, Entity* always_send = 0, bool iSendEvenIfNotChanged = false); char* MakeNameUnique(char* name); static char* RemoveNumbers(char* name); void SignalMobsByNPCID(uint32 npc_type, int signal_id); diff --git a/zone/mob.cpp b/zone/mob.cpp index 830ac9a61..256fd7df7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -131,7 +131,6 @@ Mob::Mob(const char* in_name, SetMoving(false); moved=false; m_RewindLocation = glm::vec3(); - move_tic_count = 0; _egnode = nullptr; name[0]=0; @@ -1443,8 +1442,7 @@ void Mob::SendPosition() auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; MakeSpawnUpdateNoDelta(spu); - move_tic_count = 0; - entity_list.QueueClients(this, app, true); + entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false); safe_delete(app); } @@ -1456,45 +1454,32 @@ void Mob::SendPosUpdate(uint8 iSendToSelf) { if (iSendToSelf == 2) { if (IsClient()) { - CastToClient()->FastQueuePacket(&app,false); + CastToClient()->FastQueuePacket(&app, false); } } - else - { - if(move_tic_count == RuleI(Zone, NPCPositonUpdateTicCount)) - { - entity_list.QueueClients(this, app, (iSendToSelf == 0), false); - move_tic_count = 0; - } - else if(move_tic_count % 2 == 0) - { - entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), RuleI(Range, MobPositionUpdates), nullptr, false); - move_tic_count++; - } - else { - move_tic_count++; - } + else { + entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), RuleI(Range, MobPositionUpdates), nullptr, false); } safe_delete(app); } // this is for SendPosition() -void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu){ - memset(spu,0xff,sizeof(PlayerPositionUpdateServer_Struct)); - spu->spawn_id = GetID(); - spu->x_pos = FloatToEQ19(m_Position.x); - spu->y_pos = FloatToEQ19(m_Position.y); - spu->z_pos = FloatToEQ19(m_Position.z); - spu->delta_x = NewFloatToEQ13(0); - spu->delta_y = NewFloatToEQ13(0); - spu->delta_z = NewFloatToEQ13(0); - spu->heading = FloatToEQ19(m_Position.w); - spu->animation = 0; +void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu) { + memset(spu, 0xff, sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(m_Position.x); + spu->y_pos = FloatToEQ19(m_Position.y); + spu->z_pos = FloatToEQ19(m_Position.z); + spu->delta_x = NewFloatToEQ13(0); + spu->delta_y = NewFloatToEQ13(0); + spu->delta_z = NewFloatToEQ13(0); + spu->heading = FloatToEQ19(m_Position.w); + spu->animation = 0; spu->delta_heading = NewFloatToEQ13(0); - spu->padding0002 =0; - spu->padding0006 =7; - spu->padding0014 =0x7f; - spu->padding0018 =0x5df27; + spu->padding0002 = 0; + spu->padding0006 = 7; + spu->padding0014 = 0x7f; + spu->padding0018 = 0x5df27; } diff --git a/zone/mob.h b/zone/mob.h index c1a300569..27ea48d4f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1373,7 +1373,6 @@ protected: void ClearItemFactionBonuses(); void CalculateFearPosition(); - uint32 move_tic_count; bool flee_mode; Timer flee_timer; From 59a2f0cdde4986aac63ab0a0cff06fac24dcb9e3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 11 Jul 2017 01:58:47 -0500 Subject: [PATCH 08/10] Refactor close_npcs to close_mobs for future implementations --- zone/client.cpp | 2 +- zone/client.h | 2 +- zone/client_process.cpp | 17 ++++++++++------- zone/entity.cpp | 7 +++---- zone/entity.h | 2 +- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 03e113ed0..986c2b582 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -358,7 +358,7 @@ Client::~Client() { m_tradeskill_object = nullptr; } - close_npcs.clear(); + close_mobs.clear(); if(IsDueling() && GetDuelTarget() != 0) { Entity* entity = entity_list.GetID(GetDuelTarget()); diff --git a/zone/client.h b/zone/client.h index 879e6622a..7bd02c46c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -222,7 +222,7 @@ public: Client(EQStreamInterface * ieqs); ~Client(); - std::unordered_map close_npcs; + std::unordered_map close_mobs; bool is_client_moving; //abstract virtual function implementations required by base abstract class diff --git a/zone/client_process.cpp b/zone/client_process.cpp index a5ced5d1a..08cb9a325 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -254,7 +254,7 @@ bool Client::Process() { /* Build a close range list of NPC's */ if (npc_close_scan_timer.Check()) { - close_npcs.clear(); + close_mobs.clear(); auto &mob_list = entity_list.GetMobList(); float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan)); @@ -265,10 +265,10 @@ bool Client::Process() { float distance = DistanceSquared(m_Position, mob->GetPosition()); if (mob->IsNPC()) { if (distance <= scan_range) { - close_npcs.insert(std::pair(mob->CastToNPC(), distance)); + close_mobs.insert(std::pair(mob, distance)); } else if (mob->GetAggroRange() > scan_range) { - close_npcs.insert(std::pair(mob->CastToNPC(), distance)); + close_mobs.insert(std::pair(mob, distance)); } } @@ -631,11 +631,14 @@ bool Client::Process() { // only if client is not feigned if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) { int npc_scan_count = 0; - for (auto it = close_npcs.begin(); it != close_npcs.end(); ++it) { - NPC *npc = it->first; + for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) { + Mob *mob = it->first; - if (npc->CheckWillAggro(this) && !npc->CheckAggro(this)) { - npc->AddToHateList(this, 25); + if (mob->IsClient()) + continue; + + if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) { + mob->AddToHateList(this, 25); } npc_scan_count++; } diff --git a/zone/entity.cpp b/zone/entity.cpp index eee89ce0f..2a3429f02 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2323,10 +2323,9 @@ bool EntityList::RemoveNPC(uint16 delete_id) // make sure its proximity is removed RemoveProximity(delete_id); // remove from client close lists - RemoveNPCFromClientCloseLists(npc); + RemoveMobFromClientCloseLists(npc->CastToMob()); // remove from the list npc_list.erase(it); - // remove from limit list if needed if (npc_limit_list.count(delete_id)) @@ -2336,11 +2335,11 @@ bool EntityList::RemoveNPC(uint16 delete_id) return false; } -bool EntityList::RemoveNPCFromClientCloseLists(NPC *npc) +bool EntityList::RemoveMobFromClientCloseLists(Mob *mob) { auto it = client_list.begin(); while (it != client_list.end()) { - it->second->close_npcs.erase(npc); + it->second->close_mobs.erase(mob); ++it; } return false; diff --git a/zone/entity.h b/zone/entity.h index 16249547b..036e0c419 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -279,7 +279,7 @@ public: bool RemoveTrap(uint16 delete_id); bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); - bool RemoveNPCFromClientCloseLists(NPC *npc); + bool RemoveMobFromClientCloseLists(Mob *mob); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); From d9a1cf8c7b9bde228fad35735b0f42bba8d0bde5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 11 Jul 2017 02:08:00 -0500 Subject: [PATCH 09/10] Safeguard to npc aggro scanning --- zone/client_process.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 08cb9a325..a346b7ce3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -634,6 +634,9 @@ bool Client::Process() { for (auto it = close_mobs.begin(); it != close_mobs.end(); ++it) { Mob *mob = it->first; + if (!mob) + continue; + if (mob->IsClient()) continue; From d47daa28578e4dac247d64b76586939274066eee Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 11 Jul 2017 02:42:06 -0500 Subject: [PATCH 10/10] Pointer removals --- zone/entity.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/entity.cpp b/zone/entity.cpp index 2a3429f02..52eb41165 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2282,6 +2282,9 @@ bool EntityList::RemoveMob(uint16 delete_id) auto it = mob_list.find(delete_id); if (it != mob_list.end()) { + + RemoveMobFromClientCloseLists(it->second); + if (npc_list.count(delete_id)) entity_list.RemoveNPC(delete_id); else if (client_list.count(delete_id)) @@ -2304,6 +2307,8 @@ bool EntityList::RemoveMob(Mob *delete_mob) auto it = mob_list.begin(); while (it != mob_list.end()) { if (it->second == delete_mob) { + RemoveMobFromClientCloseLists(it->second); + safe_delete(it->second); if (!corpse_list.count(it->first)) free_ids.push(it->first);