From 1c8dea909e3fbbdd77c757868db2240278de811c Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 24 Jun 2017 21:11:46 -0500 Subject: [PATCH 01/14] New rules made by developers are now automatically created when world boots up, this keeps from having to issue schema SQL updates every time rules are added. - Whenever a rule isn't present in the database, it will be automatically created - utils/sql/git/required/2017_06_24_rule_values_expand.sql required for avoid floating point value inserts --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + utils/sql/git/required/2017_06_24_rule_values_expand.sql | 2 ++ world/net.cpp | 2 ++ 4 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 utils/sql/git/required/2017_06_24_rule_values_expand.sql diff --git a/common/version.h b/common/version.h index 40efffa5a..2391862a1 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9111 +#define CURRENT_BINARY_DATABASE_VERSION 9112 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017 #else diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index fc97ffc1b..5586b9848 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -365,6 +365,7 @@ 9109|2017_04_08_doors_disable_timer.sql|SHOW COLUMNS FROM `doors` LIKE 'disable_timer'|empty| 9110|2017_04_10_graveyard.sql|show index from graveyard WHERE key_name = 'zone_id_nonunique'|empty| 9111|2017_06_24_saylink_index.sql|SHOW INDEX FROM `saylink` WHERE `key_name` = 'phrase_index'|empty| +9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2017_06_24_rule_values_expand.sql b/utils/sql/git/required/2017_06_24_rule_values_expand.sql new file mode 100644 index 000000000..2d4720007 --- /dev/null +++ b/utils/sql/git/required/2017_06_24_rule_values_expand.sql @@ -0,0 +1,2 @@ +ALTER TABLE `rule_values` +MODIFY COLUMN `rule_value` varchar(30) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `rule_name`; diff --git a/world/net.cpp b/world/net.cpp index 09511c3f2..599f01c8f 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -332,6 +332,8 @@ int main(int argc, char** argv) { database.ClearMerchantTemp(); } + RuleManager::Instance()->SaveRules(&database); + Log(Logs::General, Logs::World_Server, "Loading EQ time of day.."); TimeOfDay_Struct eqTime; time_t realtime; From 497170c4533ba9d8017122eec39deab9cb2b81c6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 25 Jun 2017 16:30:37 -0400 Subject: [PATCH 02/14] Optimize Entity::AESpell Probably could use more work, but quick testing had this method taking the least amount of time in various situations --- zone/effects.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 8d8265e32..e7933a833 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -703,6 +703,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; + const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast(center->GetPosition()); + glm::vec2 min = { position.x - dist, position.y - dist }; + glm::vec2 max = { position.x + dist, position.y + dist }; + bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); int MAX_TARGETS_ALLOWED = 4; @@ -732,13 +736,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc())) continue; + if (!IsWithinAxisAlignedBox(static_cast(curmob->GetPosition()), min, max)) + continue; - if (spells[spell_id].targettype == ST_Ring) { - dist_targ = DistanceSquared(static_cast(curmob->GetPosition()), caster->GetTargetRingLocation()); - } - else if (center) { - dist_targ = DistanceSquared(curmob->GetPosition(), center->GetPosition()); - } + dist_targ = DistanceSquared(curmob->GetPosition(), position); if (dist_targ > dist2) //make sure they are in range continue; @@ -761,9 +762,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ if (bad) { if (!caster->IsAttackAllowed(curmob, true)) continue; - if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) - continue; - if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) + if (!spells[spell_id].npc_no_los && !caster->CheckLosFN(position.x, position.y, position.z, curmob->GetSize())) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. From 3e1b75b81484ebc3137dc878e32e1cff16382d97 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 25 Jun 2017 18:18:27 -0400 Subject: [PATCH 03/14] Fix Rain target limit (massive nerf) Added rule Spells:OldRainTargets, set to true if you don't want the nerf --- common/ruletypes.h | 1 + zone/beacon.cpp | 7 +++++-- zone/beacon.h | 1 + zone/effects.cpp | 14 ++++++++++---- zone/entity.h | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e9599e09b..11a55c423 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -397,6 +397,7 @@ RULE_BOOL(Spells, FlatItemExtraSpellAmt, false) // allow SpellDmg stat to affect RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spread on applying SpellDmg RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target. +RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/beacon.cpp b/zone/beacon.cpp index 27f86fce7..e5ea9f38f 100644 --- a/zone/beacon.cpp +++ b/zone/beacon.cpp @@ -68,6 +68,7 @@ Beacon::Beacon(Mob *at_mob, int lifetime) resist_adjust = 0; spell_iterations = 0; caster_id = 0; + max_targets = 4; // default if(lifetime) remove_timer.Start(); @@ -93,10 +94,10 @@ bool Beacon::Process() ) { Mob *caster = entity_list.GetMob(caster_id); - if(caster && spell_iterations--) + if(caster && spell_iterations-- && max_targets) { bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()); //NPC AE spells do not affect the NPC caster - entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust); + entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets); } else { @@ -126,6 +127,8 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj this->resist_adjust = resist_adjust; spell_iterations = spells[spell_id].AEDuration / 2500; spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1 + if (spells[spell_id].aemaxtargets) + max_targets = spells[spell_id].aemaxtargets; spell_timer.Start(2500); spell_timer.Trigger(); } diff --git a/zone/beacon.h b/zone/beacon.h index b79ed318c..c22189dfd 100644 --- a/zone/beacon.h +++ b/zone/beacon.h @@ -56,6 +56,7 @@ protected: int16 resist_adjust; int spell_iterations; Timer spell_timer; + int max_targets; uint16 caster_id; private: diff --git a/zone/effects.cpp b/zone/effects.cpp index e7933a833..0585f3c50 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -694,7 +694,7 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate) // causes caster to hit every mob within dist range of center with // spell_id. // NPC spells will only affect other NPCs with compatible faction -void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust) +void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets) { Mob *curmob = nullptr; @@ -709,9 +709,13 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); - int MAX_TARGETS_ALLOWED = 4; - if (spells[spell_id].aemaxtargets) + if (RuleB(Spells, OldRainTargets)) + max_targets = nullptr; // ignore it! + + int MAX_TARGETS_ALLOWED = max_targets ? *max_targets : 4; + + if (!max_targets && spells[spell_id].aemaxtargets) MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets; int iCounter = 0; @@ -789,8 +793,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ } if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)... - iCounter++; + iCounter++; // should really pull out the MAX_TARGETS_ALLOWED calc so we can break early ... } + if (max_targets) + *max_targets = *max_targets - std::min(iCounter, *max_targets); // could be higher than the count } void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) diff --git a/zone/entity.h b/zone/entity.h index 94078691a..e2224d05d 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -357,7 +357,7 @@ public: void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::inventory::slotPrimary, int count = 0, bool IsFromSpell = false); void AETaunt(Client *caster, float range=0, int32 bonus_hate=0); - void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0); + void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr); void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); From c0f53647b8b7c8e354af224edc238eab1f6dd443 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 25 Jun 2017 20:37:37 -0500 Subject: [PATCH 04/14] Revert 5fac13075b7acbd685821c1c12494a1d0df321b9 until we don't creep client resend up to 4 seconds so quickly, this causes issues in higher traffic amounts and takes longer for clients to recover than needed --- common/net/daybreak_connection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/net/daybreak_connection.h b/common/net/daybreak_connection.h index 2ec7f200f..b4faf2438 100644 --- a/common/net/daybreak_connection.h +++ b/common/net/daybreak_connection.h @@ -220,7 +220,7 @@ namespace EQ resend_delay_ms = 150; resend_delay_factor = 1.5; resend_delay_min = 150; - resend_delay_max = 4000; + resend_delay_max = 1000; connect_delay_ms = 500; stale_connection_ms = 90000; connect_stale_ms = 5000; From 4f7b8e0934469d53323d8799bcc20188cc007a4b Mon Sep 17 00:00:00 2001 From: Akkadius Date: Mon, 26 Jun 2017 00:12:56 -0500 Subject: [PATCH 05/14] Add Critical Hit range (default 80) to legacy_combat.lua and fix a variable in Pet Criticals --- utils/mods/legacy_combat.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/utils/mods/legacy_combat.lua b/utils/mods/legacy_combat.lua index 9ba9eb3b0..9ab70cf4d 100644 --- a/utils/mods/legacy_combat.lua +++ b/utils/mods/legacy_combat.lua @@ -42,6 +42,7 @@ WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff); ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty); UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules); +CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); function MeleeMitigation(e) e.IgnoreDefault = true; @@ -238,9 +239,9 @@ function TryCriticalHit(e) e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100; if (self:GetGender() == 1) then - entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done)); + entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done)); else - entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done)); + entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done)); end return e; @@ -326,15 +327,15 @@ function TryCriticalHit(e) end if (crip_success) then - entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done)); + entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done)); if (defender:GetLevel() <= 55 and not defender:GetSpecialAbility(SpecialAbility.unstunable)) then defender:Emote("staggers."); defender:Stun(0); end elseif (deadlySuccess) then - entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done)); + entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done)); else - entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done)); + entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done)); end end end @@ -379,9 +380,9 @@ function TryPetCriticalHit(self, defender, hit) local entity_list = eq.get_entity_list(); critMod = critMod + GetCritDmgMod(self, hit.skill) * 2; hit.damage_done = (hit.damage_done * critMod) / 100; - entity_list:FilteredMessageClose(this, false, 200, + entity_list:FilteredMessageClose(this, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', - self:GetCleanName(), hit.damage_done)); + self:GetCleanName(), e.hit.damage_done)); end end @@ -750,4 +751,4 @@ function ApplyMeleeDamageBonus(e) e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100); return e; -end \ No newline at end of file +end From b09792812aa7496ae16aa3ccfbff632282f6ea8b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Mon, 26 Jun 2017 15:00:47 -0400 Subject: [PATCH 06/14] Final AESpell optimization pass --- zone/effects.cpp | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 0585f3c50..7dfc4e617 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -713,10 +713,15 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ if (RuleB(Spells, OldRainTargets)) max_targets = nullptr; // ignore it! - int MAX_TARGETS_ALLOWED = max_targets ? *max_targets : 4; - - if (!max_targets && spells[spell_id].aemaxtargets) - MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets; + // if we have a passed in value, use it, otherwise default to data + // detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs + int max_targets_allowed = 0; // unlimited + if (max_targets) // rains pass this in since they need to preserve the count through waves + max_targets_allowed = *max_targets; + else if (spells[spell_id].aemaxtargets) + max_targets_allowed = spells[spell_id].aemaxtargets; + else if (IsTargetableAESpell(spell_id) && bad && !isnpc) + max_targets_allowed = 4; int iCounter = 0; @@ -779,24 +784,17 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ } curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - //if we get here... cast the spell. - if (IsTargetableAESpell(spell_id) && bad) { - if (iCounter < MAX_TARGETS_ALLOWED) { - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - } - } else { - if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); - if (!spells[spell_id].aemaxtargets) - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + if (max_targets_allowed) { // if we have a limit, increment count + iCounter++; + if (iCounter >= max_targets_allowed) // we done + break; } - - if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)... - iCounter++; // should really pull out the MAX_TARGETS_ALLOWED calc so we can break early ... } - if (max_targets) - *max_targets = *max_targets - std::min(iCounter, *max_targets); // could be higher than the count + + if (max_targets && max_targets_allowed) + *max_targets = *max_targets - iCounter; } void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) From 592bbd31804a7b03f0e685059476620941994683 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 27 Jun 2017 20:38:46 -0400 Subject: [PATCH 07/14] Target AEs don't always hit target --- zone/spells.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index ef0553526..5504422e0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2237,21 +2237,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui // special ae duration spell ae_center->CastToBeacon()->AELocationSpell(this, spell_id, resist_adjust); } else { - // regular PB AE or targeted AE spell - spell_target is null if PB - if(spell_target) // this must be an AETarget spell - { - bool cast_on_target = true; - if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && spell_target->IsPetOwnerClient()) - cast_on_target = false; - if (spells[spell_id].targettype == ST_AreaClientOnly && !spell_target->IsClient()) - cast_on_target = false; - if (spells[spell_id].targettype == ST_AreaNPCOnly && !spell_target->IsNPC()) - cast_on_target = false; - - // affect the target too - if (cast_on_target) - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); - } + // unsure if we actually need this? Need to find some spell examples if(ae_center && ae_center == this && IsBeneficialSpell(spell_id)) SpellOnTarget(spell_id, this); From 539fa8b2628ed97b75638ea4f1eae0c23fa5f303 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 28 Jun 2017 02:38:20 -0500 Subject: [PATCH 08/14] Fixed issues with Z correctness when NPCs are pathing on normal grids Fixed issues with Z correctness when NPCs are engaged with players following NPC corpses should fall into the ground far less --- changelog.txt | 45 +++++++++------ common/ruletypes.h | 1 + zone/attack.cpp | 7 +++ zone/mob.cpp | 4 +- zone/mob.h | 3 + zone/mob_ai.cpp | 3 + zone/waypoints.cpp | 141 +++++++++++++-------------------------------- 7 files changed, 85 insertions(+), 119 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8edf925b0..0614ff4a8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,25 +1,36 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 6/28/2017 == +Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids +Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following +Akkadius: NPC corpses should fall into the ground far less + +== 6/25/2017 == +Akkadius: New rules made by developers are now automatically created when world boots up, this keeps + from having to issue schema SQL updates every time rules are added. + - Whenever a rule isn't present in the database, it will be automatically created +Akkadius: Sped up saylink retrieval x1000 helpful for dialogues, plugins with many saylinks + == 4/16/2017 == KLS: Merge eqstream branch - -UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs). - -TCP Server to Server connection stack completely rewritten. - -Server connections reconnect much more reliably and quickly now. - -Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/). - -Protocol behind the tcp connections has changed (see breaking changes section). - -API significantly changed and should be easier to write new servers or handlers for. - -Telnet console connection has been separated out from the current port (see breaking changes section). - -Because of changes to the TCP stack, lsreconnect and echo have been disabled. - -The server tic rate has been changed to be approx 30 fps from 500+ fps. - -Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough). + - UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs). + - TCP Server to Server connection stack completely rewritten. + - Server connections reconnect much more reliably and quickly now. + - Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/). + - Protocol behind the tcp connections has changed (see breaking changes section). + - API significantly changed and should be easier to write new servers or handlers for. + - Telnet console connection has been separated out from the current port (see breaking changes section). + - Because of changes to the TCP stack, lsreconnect and echo have been disabled. + - The server tic rate has been changed to be approx 30 fps from 500+ fps. + - Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough). - -Breaking changes: - -Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/ - -To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections: - -You should add 1 to the login section of your configuration file when connecting to a server that is using the old protocol. - -The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section. - -Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections. - -To enable telnet you need to add a telnet tag in the world section of configuration such as: + - Breaking changes: + - Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/ + - To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections: + - You should add 1 to the login section of your configuration file when connecting to a server that is using the old protocol. + - The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section. + - Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections. + - To enable telnet you need to add a telnet tag in the world section of configuration such as: == 4/1/2017 == diff --git a/common/ruletypes.h b/common/ruletypes.h index 11a55c423..945c2625a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -274,6 +274,7 @@ RULE_BOOL(Map, FixPathingZWhenLoading, true) //increases zone boot times a bit RULE_BOOL(Map, FixPathingZAtWaypoints, false) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time. RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints. RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well. +RULE_BOOL(Map, FixZWhenMoving, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor) RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply. RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. diff --git a/zone/attack.cpp b/zone/attack.cpp index 388a959c2..28ff607dc 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2388,6 +2388,13 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil entity_list.UnMarkNPC(GetID()); entity_list.RemoveNPC(GetID()); + + /* Fix Z on Corpse Creation */ + glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); + float new_z = zone->zonemap->FindBestZ(dest, nullptr); + corpse->SetFlyMode(1); + corpse->GMMove(m_Position.x, m_Position.y, new_z + 5, m_Position.w); + this->SetID(0); if (killer != 0 && emoteid != 0) diff --git a/zone/mob.cpp b/zone/mob.cpp index 64ada4b71..c411f6a3c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -112,7 +112,9 @@ Mob::Mob(const char* in_name, m_Position(position), tmHidden(-1), mitigation_ac(0), - m_specialattacks(eSpecialAttacks::None) + m_specialattacks(eSpecialAttacks::None), + fix_z_timer(1000), + fix_z_timer_engaged(100) { targeted = 0; tar_ndx=0; diff --git a/zone/mob.h b/zone/mob.h index f2516b613..a8bf043c6 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -913,6 +913,7 @@ public: float GetGroundZ(float new_x, float new_y, float z_offset=0.0); void SendTo(float new_x, float new_y, float new_z); void SendToFixZ(float new_x, float new_y, float new_z); + void FixZ(); void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } @@ -1373,6 +1374,8 @@ protected: bool flee_mode; Timer flee_timer; + Timer fix_z_timer; + Timer fix_z_timer_engaged; bool pAIControlled; bool roamer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index fd0ec5d81..405382033 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -991,6 +991,9 @@ void Mob::AI_Process() { if (engaged) { + if (moving && fix_z_timer_engaged.Check()) + this->FixZ(); + if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) SendAddPlayerState(PlayerState::Aggressive); // we are prevented from getting here if we are blind and don't have a target in range diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 64f674cde..6ceee481e 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -513,39 +513,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo m_Position.y = new_y; m_Position.z = new_z; - uint8 NPCFlyMode = 0; - - if (IsNPC()) { - if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) - NPCFlyMode = 1; - } - - //fix up pathing Z - if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) - { - if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || - (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) - { - glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); - - float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; - - if ((newz > -2000) && - std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. - { - if ((std::abs(x - m_Position.x) < 0.5) && - (std::abs(y - m_Position.y) < 0.5)) { - if (std::abs(z - m_Position.z) <= - RuleR(Map, FixPathingZMaxDeltaMoving)) - m_Position.z = z; - else - m_Position.z = newz + 1; - } - else - m_Position.z = newz + 1; - } - } - } + if(fix_z_timer.Check()) + this->FixZ(); tar_ndx++; return true; @@ -651,37 +620,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo m_Position.w = CalculateHeadingToTarget(x, y); } - uint8 NPCFlyMode = 0; - - if (IsNPC()) { - if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) - NPCFlyMode = 1; - } - - //fix up pathing Z - if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { - - if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || - (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) - { - glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); - - float newz = zone->zonemap->FindBestZ(dest, nullptr); - - if ((newz > -2000) && - std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. - { - if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { - if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) - m_Position.z = z; - else - m_Position.z = newz + 1; - } - else - m_Position.z = newz + 1; - } - } - } + if (fix_z_timer.Check()) + this->FixZ(); SetMoving(true); moved = true; @@ -769,39 +709,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ Log(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.x, m_Position.y, m_Position.z); } - uint8 NPCFlyMode = 0; - - if (IsNPC()) { - if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) - NPCFlyMode = 1; - } - - //fix up pathing Z - if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) - { - if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || - (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) - { - glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); - - float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; - - Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z); - - if ((newz > -2000) && - std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. - { - if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { - if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) - m_Position.z = z; - else - m_Position.z = newz + 1; - } - else - m_Position.z = newz + 1; - } - } - } + if (fix_z_timer.Check()) + this->FixZ(); //OP_MobUpdate if ((old_test_vector != test_vector) || tar_ndx>20) { //send update @@ -943,9 +852,6 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { m_Position.y = new_y; m_Position.z = new_z + 0.1; - //fix up pathing Z, this shouldent be needed IF our waypoints - //are corrected instead - if (zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) { if (!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || @@ -955,7 +861,7 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { float newz = zone->zonemap->FindBestZ(dest, nullptr); - Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z); + Log(Logs::Moderate, Logs::Pathing, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. m_Position.z = newz + 1; @@ -963,6 +869,39 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { } } +void Mob::FixZ() { + BenchTimer timer; + timer.reset(); + + if (zone->HasMap() && RuleB(Map, FixZWhenMoving) && (flymode != 1 && flymode != 2)) + { + if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) + { + glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); + + float new_z = zone->zonemap->FindBestZ(dest, nullptr); + + auto duration = timer.elapsed(); + + Log( + Logs::Moderate, + Logs::Pathing, + "Mob::FixZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf", + this->GetCleanName(), + new_z, + m_Position.x, + m_Position.y, + m_Position.z, + duration + ); + + if ((new_z > -2000) && std::abs(new_z - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) + m_Position.z = new_z + 1; + } + } +} + int ZoneDatabase::GetHighestGrid(uint32 zoneid) { std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); From cbbd01b39148fd1c6be13779fcf5074cb0880378 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 28 Jun 2017 13:27:37 -0400 Subject: [PATCH 09/14] Quick fix for NPC attack skill issue --- zone/attack.cpp | 5 ++--- zone/mob.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 28ff607dc..c210e37dc 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -53,9 +53,8 @@ extern WorldServer worldserver; extern EntityList entity_list; extern Zone* zone; -EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon) +EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills:SkillType skillinuse) { - EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt; // Determine animation int type = 0; if (weapon && weapon->IsClassCommon()) { @@ -1924,7 +1923,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //do attack animation regardless of whether or not we can hit below int16 charges = 0; EQEmu::ItemInstance weapon_inst(weapon, charges); - my_hit.skill = AttackAnimation(Hand, &weapon_inst); + my_hit.skill = AttackAnimation(Hand, &weapon_inst, my_hit.skill); //basically "if not immune" then do the attack if (weapon_damage > 0) { diff --git a/zone/mob.h b/zone/mob.h index a8bf043c6..109cd3656 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -233,7 +233,7 @@ public: inline bool SeeImprovedHide() const { return see_improved_hide; } bool IsInvisible(Mob* other = 0) const; void SetInvisible(uint8 state); - EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon); + EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt); //Song bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); From 09ccd23d0bc8a82b7840e14fd281dbf163884fc3 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 28 Jun 2017 13:30:20 -0400 Subject: [PATCH 10/14] Fix syntax issue --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index c210e37dc..04cb65c9a 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -53,7 +53,7 @@ extern WorldServer worldserver; extern EntityList entity_list; extern Zone* zone; -EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills:SkillType skillinuse) +EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse) { // Determine animation int type = 0; From 4d2825d8179d9cd0b690316b400df16051c256df Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 28 Jun 2017 16:43:38 -0500 Subject: [PATCH 11/14] Make sure we also fix Z when mobs flee up/down stairs --- zone/mob_ai.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 405382033..07f89ca63 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -743,6 +743,10 @@ void Client::AI_Process() if(RuleB(Combat, EnableFearPathing)){ if(currently_fleeing) { + + if (fix_z_timer_engaged.Check()) + this->FixZ(); + if(IsRooted()) { //make sure everybody knows were not moving, for appearance sake if(IsMoving()) @@ -782,6 +786,7 @@ void Client::AI_Process() } return; } + } } From e0237ce5269425a9e2abdc8e7c5d7d9f1b18535b Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 28 Jun 2017 19:58:06 -0400 Subject: [PATCH 12/14] Quick fix for AE LOS issue --- zone/effects.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 7dfc4e617..95eb44ac8 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -771,7 +771,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ if (bad) { if (!caster->IsAttackAllowed(curmob, true)) continue; - if (!spells[spell_id].npc_no_los && !caster->CheckLosFN(position.x, position.y, position.z, curmob->GetSize())) + if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob)) + continue; + if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. From 5a9744b429094fcbe791f293949f2e8e39568be5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 28 Jun 2017 21:23:02 -0500 Subject: [PATCH 13/14] Add a fail-safe for dynamic zone shutdown timers --- zone/net.cpp | 3 +++ zone/zone.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/zone/net.cpp b/zone/net.cpp index f51d50bb4..eafdde807 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -541,6 +541,9 @@ int main(int argc, char** argv) { if (previous_loaded && !current_loaded) { process_timer.Stop(); process_timer.Start(1000, true); + + uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); + zone->StartShutdownTimer(shutdown_timer); } else if (!previous_loaded && current_loaded) { process_timer.Stop(); diff --git a/zone/zone.cpp b/zone/zone.cpp index 086754d55..6a0754dea 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1414,11 +1414,11 @@ bool Zone::HasWeather() void Zone::StartShutdownTimer(uint32 set_time) { if (set_time > autoshutdown_timer.GetRemainingTime()) { - if (set_time == (RuleI(Zone, AutoShutdownDelay))) - { + if (set_time == (RuleI(Zone, AutoShutdownDelay))) { set_time = database.getZoneShutDownDelay(GetZoneID(), GetInstanceVersion()); } - autoshutdown_timer.Start(set_time, false); + autoshutdown_timer.SetTimer(set_time); + Log(Logs::General, Logs::Zone_Server, "Zone::StartShutdownTimer set to %u", set_time); } } From 9b1a449fbaa5a9aee5173fda93b269787c827647 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Wed, 28 Jun 2017 21:44:31 -0500 Subject: [PATCH 14/14] Fix Z when following during pull, not when engaged and stationary or moving slightly --- zone/mob_ai.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 07f89ca63..20d22c42f 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -996,8 +996,11 @@ void Mob::AI_Process() { if (engaged) { + /* Fix Z when following during pull, not when engaged and stationary */ if (moving && fix_z_timer_engaged.Check()) - this->FixZ(); + if(this->GetTarget()) + if(DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition()) > 50) + this->FixZ(); if (!(m_PlayerState & static_cast(PlayerState::Aggressive))) SendAddPlayerState(PlayerState::Aggressive);