From b65d3c85b668f85370a3db625b8a6a852ecc5f6e Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 10:42:11 -0400 Subject: [PATCH 01/22] Implemented support for spells_new fields InCombat, OutofCombat Required SQL to rename and add new fields to spells_new table. --- changelog.txt | 9 +++++++++ common/shareddb.cpp | 12 ++++++++++-- common/spdat.h | 27 ++++++++++++++++---------- zone/StringIDs.h | 4 ++++ zone/spells.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/changelog.txt b/changelog.txt index 464669fd5..2847b92e6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,14 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 08/02/2014 == +Kayen: Implemented spell_news fields +- npc_no_los (check if LOS is required for spells) +- InCombat, OutofCombat - Used together to restrict spells to only be cast while +in/out of combat (beneficial) or if target is in/out of combat (detrimental). +Many new fields added and defined for future development. + +Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql + == 07/31/2014 == Uleat: More inventory slot constant conversions. This should be the bulk of everything..but, due to the size of the server code, there may be some hidden ones. (client_packet.cpp and the client translators still need a thorough review.) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index b93b57710..585576f17 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1726,6 +1726,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].EndurCost=atoi(row[166]); sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; sp[tempid].HateAdded=atoi(row[173]); sp[tempid].EndurUpkeep=atoi(row[174]); sp[tempid].numhitstype = atoi(row[175]); @@ -1745,15 +1746,22 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].directional_end = (float)atoi(row[195]); sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].rank = atoi(row[208]); sp[tempid].powerful_flag=atoi(row[209]); sp[tempid].CastRestriction = atoi(row[211]); sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].NotOutofCombat = atoi(row[213]) != 0; - sp[tempid].NotInCombat = atoi(row[214]) != 0; + sp[tempid].InCombat = atoi(row[213]) != 0; + sp[tempid].OutofCombat = atoi(row[214]) != 0; sp[tempid].aemaxtargets = atoi(row[218]); sp[tempid].maxtargets = atoi(row[219]); sp[tempid].persistdeath = atoi(row[224]) != 0; + sp[tempid].min_dist = atof(row[227]); + sp[tempid].min_dist_mod = atof(row[228]); + sp[tempid].max_dist = atof(row[229]); + sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].min_range = atoi(row[231]); sp[tempid].DamageShieldType = 0; } mysql_free_result(result); diff --git a/common/spdat.h b/common/spdat.h index 927550fc6..510817121 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -697,8 +697,8 @@ struct SPDat_Spell_Struct /* 164 */ // for most spells this appears to mimic ResistDiff /* 166 */ int EndurCost; /* 167 */ int8 EndurTimerIndex; -/* 168 */ //int IsDisciplineBuff; //Will goto the combat window when cast -/* 169 */ +/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast +/* 169 - 172*/ //These are zero for ALL spells /* 173 */ int HateAdded; /* 174 */ int EndurUpkeep; /* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. @@ -721,23 +721,30 @@ struct SPDat_Spell_Struct /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones -/* 201 - 202 */ +/* 201 */ int viral_range; +/* 202 */ /* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented) /* 204 - 206 */ /* 207 */ int spellgroup; -/* 208 */ // int rank - increments AA effects with same name +/* 208 */ int rank; //increments AA effects with same name /* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though /* 210 */ // bool DurationFrozen; ??? /* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat /* 212 */ bool AllowRest; -/* 213 */ bool NotOutofCombat; //Fail if cast out of combat -/* 214 */ bool NotInCombat; //Fail if cast in combat +/* 213 */ bool InCombat; //Allow spell if target is in combat +/* 214 */ bool OutofCombat; //Allow spell if target is out of combat /* 215 - 217 */ -/* 219 */ int aemaxtargets; // -/* 219 */ int maxtargets; // is used for beam and ring spells for target # limits (not implemented) -/* 220 - 223 */ +/* 218 */ int aemaxtargets; //Is used for various AE effects +/* 219 */ int maxtargets; //Is used for beam and ring spells for target # limits (not implemented) +/* 220 - 223 */ /* 224 */ bool persistdeath; // buff doesn't get stripped on death -/* 225 - 236 */ // Not in DB +/* 225 - 226 */ +/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) +/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) +/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) +/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) +/* 231 */ int min_range; //Min casting range +/* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 9a222d276..5e5d16bb2 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -316,6 +316,10 @@ #define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. #define PET_NOT_CASTING 9264 //Not casting spells, Master. #define PET_CASTING 9291 //Casting spells normally, Master. +#define NO_CAST_IN_COMBAT 9190 //You can not cast this spell while in combat. +#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat. +#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat. +#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat. #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. diff --git a/zone/spells.cpp b/zone/spells.cpp index 305156b63..6757a7ba6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1384,6 +1384,52 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce return false; } + //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) + if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && spell_target->IsEngaged()) || + (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && IsEngaged()) || + (IsClient() && CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_IN_COMBAT); + else + Message_StringID(13,NO_CAST_IN_COMBAT); + + return false; + } + } + } + + //Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) + else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) || + (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && !IsEngaged()) || + (IsClient() && !CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT); + else + Message_StringID(13,NO_CAST_OUT_OF_COMBAT); + + return false; + } + } + } + switch (targetType) { // single target spells From 9c92c5dbe4af5a57a7b4af82c8768bcf2a688a5b Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 10:43:48 -0400 Subject: [PATCH 02/22] SQL --- .../sql/git/required/2014_08_02_spells_new.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/utils/sql/git/required/2014_08_02_spells_new.sql b/utils/sql/git/required/2014_08_02_spells_new.sql new file mode 100644 index 000000000..f4e74158a --- /dev/null +++ b/utils/sql/git/required/2014_08_02_spells_new.sql @@ -0,0 +1,18 @@ +-- spells new talbe update +ALTER TABLE `spells_new` CHANGE `NotOutofCombat` `InCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `NotInCombat` `OutofCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field201` `viral_range` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field218` `aemaxtargets` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` ADD `field225` int( 11 ) NOT NULL DEFAULT '0' AFTER `persistdeath`; +ALTER TABLE `spells_new` ADD `field226` int( 11 ) NOT NULL DEFAULT '0' AFTER `field225`; +ALTER TABLE `spells_new` ADD `min_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `field226`; +ALTER TABLE `spells_new` ADD `min_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist`; +ALTER TABLE `spells_new` ADD `max_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist_mod`; +ALTER TABLE `spells_new` ADD `max_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `max_dist`; +ALTER TABLE `spells_new` ADD `min_range` int( 11 ) NOT NULL DEFAULT '0' AFTER `max_dist_mod`; +ALTER TABLE `spells_new` ADD `field232` int( 11 ) NOT NULL DEFAULT '0' AFTER `min_range`; +ALTER TABLE `spells_new` ADD `field233` int( 11 ) NOT NULL DEFAULT '0' AFTER `field232`; +ALTER TABLE `spells_new` ADD `field234` int( 11 ) NOT NULL DEFAULT '0' AFTER `field233`; +ALTER TABLE `spells_new` ADD `field235` int( 11 ) NOT NULL DEFAULT '0' AFTER `field234`; +ALTER TABLE `spells_new` ADD `field236` int( 11 ) NOT NULL DEFAULT '0' AFTER `field235`; + From 52d92b71814e6e364e0d5ada187c71f53238ab9c Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sat, 2 Aug 2014 21:10:44 -0400 Subject: [PATCH 03/22] Implemented broad support for fields min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. This implemented in a broad way to function with spells that would make sense to scale. Some work will still be needed on this. Be aware if making custom spells not everything will work and certain effects just should not be included (use common sense). --- changelog.txt | 4 +++- common/spdat.cpp | 9 +++++++++ common/spdat.h | 1 + zone/effects.cpp | 7 ++++++- zone/groups.cpp | 1 + zone/hate_list.cpp | 6 +++++- zone/mob.cpp | 1 + zone/mob.h | 4 ++++ zone/spell_effects.cpp | 31 ++++++++++++++++++++++++++++++- zone/spells.cpp | 10 ++++++++-- 10 files changed, 68 insertions(+), 6 deletions(-) diff --git a/changelog.txt b/changelog.txt index 2847b92e6..85fa4ecb5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,7 +5,9 @@ Kayen: Implemented spell_news fields - npc_no_los (check if LOS is required for spells) - InCombat, OutofCombat - Used together to restrict spells to only be cast while in/out of combat (beneficial) or if target is in/out of combat (detrimental). -Many new fields added and defined for future development. +-min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. +*This will require further work to fully implement but will work with 90% of live spells as is. +*If making custom spells do not include effects that can't be scaled (like a spell trigger) Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/common/spdat.cpp b/common/spdat.cpp index 75f1e9b88..8e93f4e1f 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1040,6 +1040,15 @@ bool IsCastonFadeDurationSpell(uint16 spell_id) return false; } +bool IsPowerDistModSpell(uint16 spell_id) +{ + if (IsValidSpell(spell_id) && + (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) + return true; + + return false; +} + uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) diff --git a/common/spdat.h b/common/spdat.h index 510817121..ec068585c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -845,6 +845,7 @@ bool IsPersistDeathSpell(uint16 spell_id); bool IsSuspendableSpell(uint16 spell_id); uint32 GetMorphTrigger(uint32 spell_id); bool IsCastonFadeDurationSpell(uint16 spell_id); +bool IsPowerDistModSpell(uint16 spell_id); uint32 GetPartialMeleeRuneReduction(uint32 spell_id); uint32 GetPartialMagicRuneReduction(uint32 spell_id); uint32 GetPartialMeleeRuneAmount(uint32 spell_id); diff --git a/zone/effects.cpp b/zone/effects.cpp index c298974db..27cccd0a9 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -740,6 +740,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; + float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); @@ -755,7 +756,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (curmob == caster && !affect_caster) //watch for caster too continue; - if (center->DistNoRoot(*curmob) > dist2) //make sure they are in range + + dist_targ = center->DistNoRoot(*curmob); + if (dist_targ > dist2) //make sure they are in range continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); @@ -786,6 +789,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; } + curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); + //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && bad) { if (iCounter < MAX_TARGETS_ALLOWED) { diff --git a/zone/groups.cpp b/zone/groups.cpp index 6aee5f38b..c9f6e9127 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -674,6 +674,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { { distance = caster->DistNoRoot(*members[z]); if(distance <= range2) { + members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index bb39d224a..f3de52544 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -567,20 +567,24 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) //So keep a list of entity ids and look up after std::list id_list; range = range * range; + float dist_targ = 0; auto iterator = list.begin(); while (iterator != list.end()) { tHateEntry *h = (*iterator); if(range > 0) { - if(caster->DistNoRoot(*h->ent) <= range) + dist_targ = caster->DistNoRoot(*h->ent); + if(dist_targ <= range) { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ); } } else { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, 0, caster); } ++iterator; } diff --git a/zone/mob.cpp b/zone/mob.cpp index a55390050..24b691e36 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -182,6 +182,7 @@ Mob::Mob(const char* in_name, has_numhits = false; has_MGB = false; has_ProjectIllusion = false; + SpellPowerDistanceMod = 0; if(in_aa_title>0) aa_title = in_aa_title; diff --git a/zone/mob.h b/zone/mob.h index 1793e0864..15f1728ec 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -615,6 +615,9 @@ public: bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); int16 GetSlowMitigation() const {return slow_mitigation;} + void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); + inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; + inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); @@ -1114,6 +1117,7 @@ protected: bool has_numhits; bool has_MGB; bool has_ProjectIllusion; + int16 SpellPowerDistanceMod; // Bind wound Timer bindwound_timer; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0ee88a154..864bf004c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -186,6 +186,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].numhits = numhit; } + bool _IsPowerDistModSpell = false; + if (IsPowerDistModSpell(spell_id)) + _IsPowerDistModSpell = true; + else + SetSpellPowerDistanceMod(0); + // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) { @@ -198,6 +204,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); + if (_IsPowerDistModSpell) + effect_value += (effect_value*(GetSpellPowerDistanceMod()/100)/100); + #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; #endif @@ -6444,4 +6453,24 @@ bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ return false; } - \ No newline at end of file + +void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) +{ + if (IsPowerDistModSpell(spell_id)){ + + float distance = 0; + + if (caster && !range) + distance = caster->CalculateDistance(GetX(), GetY(), GetZ()); + else + distance = sqrt(range); + + float dm_range = spells[spell_id].max_dist - spells[spell_id].min_dist; + float dm_mod_interval = spells[spell_id].max_dist_mod - spells[spell_id].min_dist_mod; + float dist_from_min = distance - spells[spell_id].min_dist; + float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); + mod *= 100.0f; + + SetSpellPowerDistanceMod(static_cast(mod)); + } +} \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index 6757a7ba6..f5bd8f09d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1915,6 +1915,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 Message_StringID(13, TARGET_OUT_OF_RANGE); return(false); } + + spell_target->CalcSpellPowerDistanceMod(spell_id, dist2); } // @@ -2114,16 +2116,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || (heading_to_target >= 0.0f && heading_to_target <= angle_end)) { - if(CheckLosFN(spell_target)) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + } } } else { if(heading_to_target >= angle_start && heading_to_target <= angle_end) { - if(CheckLosFN((*iter))) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + } } } ++iter; From ef982b9ce236d83ebe5a5e576e2eadbd9ddb3d85 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 08:21:47 -0400 Subject: [PATCH 04/22] Implemented 'min_range' field, sets a min range that you must be away from target for spell to land. --- changelog.txt | 1 + common/shareddb.cpp | 6 +++--- common/spdat.h | 2 +- zone/StringIDs.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/changelog.txt b/changelog.txt index 85fa4ecb5..16d63a05e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ in/out of combat (beneficial) or if target is in/out of combat (detrimental). -min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. *This will require further work to fully implement but will work with 90% of live spells as is. *If making custom spells do not include effects that can't be scaled (like a spell trigger) +- min_rage sets minimum distance range that must be away from target. Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 585576f17..a64fef404 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1742,8 +1742,8 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].viral_targets = atoi(row[191]); sp[tempid].viral_timer = atoi(row[192]); sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = (float)atoi(row[194]); - sp[tempid].directional_end = (float)atoi(row[195]); + sp[tempid].directional_start = static_cast(atoi(row[194])); + sp[tempid].directional_end = static_cast(atoi(row[195])); sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; sp[tempid].viral_range = atoi(row[201]); @@ -1761,7 +1761,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].min_dist_mod = atof(row[228]); sp[tempid].max_dist = atof(row[229]); sp[tempid].max_dist_mod = atof(row[230]); - sp[tempid].min_range = atoi(row[231]); + sp[tempid].min_range = static_cast(atoi(row[231])); sp[tempid].DamageShieldType = 0; } mysql_free_result(result); diff --git a/common/spdat.h b/common/spdat.h index ec068585c..aaf29e61a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -743,7 +743,7 @@ struct SPDat_Spell_Struct /* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) /* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) /* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -/* 231 */ int min_range; //Min casting range +/* 231 */ float min_range; //Min casting range /* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; diff --git a/zone/StringIDs.h b/zone/StringIDs.h index 5e5d16bb2..b1e13edca 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -249,6 +249,7 @@ #define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. #define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. +#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. From f3710856ad59e6e3c456a5d4429119b9f1f64fad Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:04:55 -0400 Subject: [PATCH 05/22] min_range field --- zone/effects.cpp | 3 +++ zone/entity.cpp | 10 +++++----- zone/entity.h | 2 +- zone/groups.cpp | 3 ++- zone/hate_list.cpp | 3 ++- zone/spell_effects.cpp | 4 ++-- zone/spells.cpp | 9 ++++++++- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/zone/effects.cpp b/zone/effects.cpp index 27cccd0a9..1b1eeb2ea 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -740,6 +740,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); @@ -760,6 +761,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ dist_targ = center->DistNoRoot(*curmob); if (dist_targ > dist2) //make sure they are in range continue; + if (dist_targ < min_range2) //make sure they are in range + continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); if (bad) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 3d316585d..5ff19847f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4538,7 +4538,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) return ClosestMob; } -void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list) +void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list) { auto it = mob_list.begin(); while (it != mob_list.end()) { @@ -4547,15 +4547,15 @@ void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, ++it; continue; } - int32 x_diff = ptr->GetX() - start->GetX(); - int32 y_diff = ptr->GetY() - start->GetY(); - int32 z_diff = ptr->GetZ() - start->GetZ(); + float x_diff = ptr->GetX() - start->GetX(); + float y_diff = ptr->GetY() - start->GetY(); + float z_diff = ptr->GetZ() - start->GetZ(); x_diff *= x_diff; y_diff *= y_diff; z_diff *= z_diff; - if ((x_diff + y_diff) <= (radius * radius)) + if ((x_diff + y_diff) <= (radius * radius) && (x_diff + y_diff) >= (min_radius * min_radius)) if(z_diff <= (height * height)) m_list.push_back(ptr); diff --git a/zone/entity.h b/zone/entity.h index 8b94b0e0a..c1ba0936c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -406,7 +406,7 @@ public: void GetObjectList(std::list &o_list); void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); - void GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list); + void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/groups.cpp b/zone/groups.cpp index c9f6e9127..d948eb476 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -658,6 +658,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { range = caster->GetAOERange(spell_id); float range2 = range*range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; // caster->SpellOnTarget(spell_id, caster); @@ -673,7 +674,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { else if(members[z] != nullptr) { distance = caster->DistNoRoot(*members[z]); - if(distance <= range2) { + if(distance <= range2 && distance >= min_range2) { members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index f3de52544..1b585ca32 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -567,6 +567,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) //So keep a list of entity ids and look up after std::list id_list; range = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float dist_targ = 0; auto iterator = list.begin(); while (iterator != list.end()) @@ -575,7 +576,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) if(range > 0) { dist_targ = caster->DistNoRoot(*h->ent); - if(dist_targ <= range) + if(dist_targ <= range && dist_targ >= min_range2) { id_list.push_back(h->ent->GetID()); h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 864bf004c..2312614bc 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -205,7 +205,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) effect_value = GetMaxHP(); if (_IsPowerDistModSpell) - effect_value += (effect_value*(GetSpellPowerDistanceMod()/100)/100); + effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; @@ -6471,6 +6471,6 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; - SetSpellPowerDistanceMod(static_cast(mod)); + SetSpellPSetSpellPowerDistanceMod(static_cast(mod)); } } \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index f5bd8f09d..ca6d80f6f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1909,12 +1909,19 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 //casting a spell on somebody but ourself, make sure they are in range float dist2 = DistNoRoot(*spell_target); float range2 = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; if(dist2 > range2) { //target is out of range. mlog(SPELLS__CASTING, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); Message_StringID(13, TARGET_OUT_OF_RANGE); return(false); } + else if (dist2 < min_range2){ + //target is too close range. + mlog(SPELLS__CASTING, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2); + Message_StringID(13, TARGET_TOO_CLOSE); + return(false); + } spell_target->CalcSpellPowerDistanceMod(spell_id, dist2); } @@ -2100,7 +2107,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 std::list targets_in_range; std::list::iterator iter; - entity_list.GetTargetsForConeArea(this, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); iter = targets_in_range.begin(); while(iter != targets_in_range.end()) { From 4b7fa0654c00b0fd4d30d182ca752db82ca20908 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:32:36 -0400 Subject: [PATCH 06/22] fix --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2312614bc..dc4de2409 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -204,7 +204,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); - if (_IsPowerDistModSpell) + if (_IsPowerDistModSpell && GetSpellPowerDistanceMod()) effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM From 92a1abbbee67ce6507c27be147edbcd821db3bdc Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 3 Aug 2014 16:36:44 -0400 Subject: [PATCH 07/22] fix --- zone/spell_effects.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index dc4de2409..078827b78 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -186,10 +186,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].numhits = numhit; } - bool _IsPowerDistModSpell = false; - if (IsPowerDistModSpell(spell_id)) - _IsPowerDistModSpell = true; - else + if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); // iterate through the effects in the spell @@ -204,7 +201,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); - if (_IsPowerDistModSpell && GetSpellPowerDistanceMod()) + if (GetSpellPowerDistanceMod()) effect_value = effect_value*(GetSpellPowerDistanceMod()/100); #ifdef SPELL_EFFECT_SPAM From 13f3c13c0ed3b00ef071c5aa2b6be05ba92cf4b3 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 4 Aug 2014 21:28:56 -0400 Subject: [PATCH 08/22] Added a dev script for cross-referencing server-to-client opcodes --- utils/scripts/.gitignore | 1 + utils/scripts/opcode_handlers.py | 475 +++++++++++++++++++++++++++++++ 2 files changed, 476 insertions(+) create mode 100644 utils/scripts/.gitignore create mode 100644 utils/scripts/opcode_handlers.py diff --git a/utils/scripts/.gitignore b/utils/scripts/.gitignore new file mode 100644 index 000000000..eba9f59cb --- /dev/null +++ b/utils/scripts/.gitignore @@ -0,0 +1 @@ +opcode_handlers_output \ No newline at end of file diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py new file mode 100644 index 000000000..cae5fdc5c --- /dev/null +++ b/utils/scripts/opcode_handlers.py @@ -0,0 +1,475 @@ +#! /usr/bin/env python +# untested on Linux + +# This script generates cross-references to show associated (handled) opcodes between the server and client. +# It will generate files for each client found and provide some basic information..such as opcode names and +# values, server handler and whether opcodes are translated on tx/rx. +# +# It's currently limited to the 'Zone' server..but, can be expounded upon to include other servers, clients +# and other criteria and features. + + +import os +#import pickle + +from time import time, ctime + + +VERBOSE = True # pipes relativistic information to the console window +DEBUG = False # setting this to 'True' will pipe more information to the console window than you would ever want! + +repopath = os.getcwd() +repopath = repopath[:-14] # '\utils\scripts' - because I'm lazy and don't want to do it the right way... + +clients = [ '6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF' ] +servers = [ 'Login', 'World', 'Zone' ] + +clientops = {} +serverops = {} + +encodes = [] +decodes = [] + +handlers = {} +outstreams = {} + + +def main() : + print('Loading source data...') + + if not LoadServerOpcodes() : + print('Error! Could not load server opcodes') + return + + if not LoadClientOpcodes() : + print('Error! Could not load client opcodes') + return + + if not LoadClientTranslators() : + print('Error! Could not load client translators') + return + + if not LoadServerHandlers() : + print('Error! Could not load server handlers') + return + + print('Creating output streams...') + + if not CreateOutputDirectory() : + print('Output directory already exists or could not be created...') + #return # existing directory returns a failure..don't exit here..CreateOutputStreams() will catch makdir() problems + + if not CreateOutputStreams() : + print('Error! Could not open output files') + return + + print('Parsing opcode data...') + + if not ParseOpcodeData() : + print('Error! Could not parse opcode data') + return + + print('Destroying output streams...') + + if not DestroyOutputStreams() : + print('Error! Could not close output files') + return + + +def LoadServerOpcodes() : + # Server opcodes are auto-enumerated with a starting reference of '1' + try : + filename = '{0}{1}{2}'.format(repopath, '\common', '\emu_oplist.h') + + if VERBOSE or DEBUG : print(filename) + + with open(filename, 'r') as datafile : + value = 0 + serverops['Null'] = value + + while True : + begin = 0 + end = 0 + + dataline = datafile.readline() + + if not dataline : break + if not dataline[:1] == 'N' : continue + + while not dataline[begin:(begin + 1)] == '(' : + if begin >= len(dataline) : break + else : begin = begin + 1 + + end = begin + 1 + + while not dataline[(end - 1):end] == ')' : + if end >= len(dataline) : break + else : end = end + 1 + + begin = begin + 1 # adjust out the '(' + end = end - 1 # adjust out the ')' + + if begin >= end or begin >= len(dataline) : continue + if end >= len(dataline) : continue + + value = value + 1 + + serverops[dataline[begin:end]] = value + + if DEBUG : print('({0}, {1}) = {2}'.format('Server', dataline[begin:end], serverops[dataline[begin:end]])) + + datafile.close() + + except : return False + + return True + + +def LoadClientOpcodes() : + badclients = [] + + for client in clients : + try : + filename = '{0}{1}{2}{3}{4}'.format(repopath, '\utils\patches', '\patch_', client, '.conf') + + if VERBOSE or DEBUG : print(filename) + + with open(filename, 'r') as datafile : + while True : + kbegin = 0 + vbegin = 0 + kend = 0 + vend = 0 + + dataline = datafile.readline() + + if not dataline : break + if not dataline[0:3] == 'OP_' : continue + + kend = kbegin + 3 + + while not dataline[(kend - 1):kend] == '=' : + if kend >= len(dataline) : break + else : kend = kend + 1 + + kend = kend - 1 # adjust out the '=' + vbegin = kend + 1 + vend = vbegin + 6 + + if dataline[vbegin:vend] == '0x0000' : continue + + clientops[client, dataline[kbegin:kend]] = dataline[vbegin:vend] + + if DEBUG : print('({0}, {1}) = {2}'.format(client, dataline[kbegin:kend], clientops[client, dataline[kbegin:kend]])) + + datafile.close() + + except : badclients.append(client) + + if len(badclients) > 0 : + badentries = [] + + for badclient in badclients : + if VERBOSE or DEBUG : print('Deleting client {0} from search criteria...'.format(badclient)) + + clients.remove(badclient) + + if VERBOSE or DEBUG : print('Deleting any partial entries for client {0}...'.format(badclient)) + + for entry in clientops.keys() : + if entry[0] == client : + badentries.append(entry) + + for badentry in badentries : + del clientops[badentry] + + if len(clients) == 0 : + return False + + return True + + +def LoadClientTranslators() : + for client in clients : + try : + if client == '6.2' : shortname = '{0}'.format('\Client62_ops.h') + else : shortname = '{0}{1}{2}'.format(chr(92), client, '_ops.h') + + filename = '{0}{1}{2}'.format(repopath, '\common\patches', shortname) + + if VERBOSE or DEBUG : print(filename) + + with open(filename, 'r') as datafile : + while True: + begin = 0 + end = 0 + + dataline = datafile.readline() + + if not dataline : break + if not dataline[:1] == 'E' and not dataline[0:1] == 'D' : continue + + while not dataline[begin:(begin + 1)] == '(' : + if begin >= len(dataline) : break + else : begin = begin + 1 + + end = begin + 1 + + while not dataline[(end - 1):end] == ')' : + if end >= len(dataline) : break + else : end = end + 1 + + begin = begin + 1 # adjust out the '(' + end = end - 1 # adjust out the ')' + + if begin >= end or begin >= len(dataline) : continue + if end >= len(dataline) : continue + + if dataline[:1] == 'E' : + encodes.append((client, dataline[begin:end])) + + if DEBUG : print('ENCODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], encodes.index((client, dataline[begin:end])))) + elif dataline[:1] == 'D' : + decodes.append((client, dataline[begin:end])) + + if DEBUG : print('DECODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], decodes.index((client, dataline[begin:end])))) + + datafile.close() + + except : return False # TODO: need to handle + + if len(encodes) == 0 and len(decodes) == 0 : + return False + + return True + + +def LoadServerHandlers() : + # TODO: handle remarked out definitions; add other servers, if possible + try : + filename = '{0}{1}{2}'.format(repopath, '\zone', '\client_packet.cpp') + + if VERBOSE or DEBUG : print(filename) + + with open(filename, 'r') as datafile : + dataline = datafile.readline() + + while not dataline[:19] == 'void MapOpcodes() {' : + dataline = datafile.readline() + + if not dataline : break + + while True : + kbegin = 0 + vbegin = 0 + kend = 0 + vend = 0 + + dataline = datafile.readline() + + if not dataline : break + if dataline[0:1] == '}' : break + + kbegin = dataline.find('OP_') + + if kbegin == -1 : continue + + kend = kbegin + 1 + + while not dataline[kend:(kend + 1)] == ']' : + if kend >= len(dataline) : break + else : kend = kend + 1 + + vbegin = dataline.find('&Client::') + + if vbegin == -1 : continue + + vbegin = vbegin + 1 + vend = vbegin + 9 + + while not dataline[vend:(vend + 1)] == ';' : + if vend >= len(dataline) : break + else : vend = vend + 1 + + handlers['Zone', dataline[kbegin:kend]] = dataline[vbegin:vend] + + if DEBUG : print('({0}, {1}) = {2}'.format('Zone', dataline[kbegin:kend], handlers['Zone', dataline[kbegin:kend]])) + + datafile.close() + + except : return False # should probably do the same for this (badservers) as done for badclients above + + return True + + +def CreateOutputDirectory() : + try : + outputpath = '{0}{1}'.format(repopath, '\utils\scripts\opcode_handlers_output') + + if VERBOSE or DEBUG : print(outputpath) + + os.mkdir(outputpath) + + return True + + except : return False + + +def CreateOutputStreams() : + try : + for client in clients : + filename = '{0}{1}{2}{3}{4}'.format(repopath, '\utils\scripts\opcode_handlers_output', chr(92), client, '_opcode_handlers.txt') + + if VERBOSE or DEBUG : print(filename) + + outstreams[client] = open(filename, 'w') + + outstreams[client].write('******************************************************\n') + outstreams[client].write('** Opcode-Handler analysis for \'{0}\' client\n'.format(client)) + outstreams[client].write('** script-generated file @ {0}\n'.format(ctime(time()))) + outstreams[client].write('**\n') + outstreams[client].write('** (only cross-linked (active) opcodes are listed)\n') + outstreams[client].write('******************************************************\n\n') + + except : + for client in clients : + if client in outstreams.keys() : + outstreams[client].close() + del outstreams[client] + + return False + + return True + + +def ParseOpcodeData() : + serveropnames = [] + + for serveropkey in serverops.keys() : + if serveropkey == 'Null' : continue + + if DEBUG : print('->ServerOpKey: {0}'.format(serveropkey)) + + serveropnames.append(serveropkey) + + if len(serveropnames) == 0 : return False + + for server in servers : + if server == 'Login' or server == 'World' : continue # Login, World not implemented yet + + handleropnames = [] + + for handlerkey in handlers.keys() : + if handlerkey[0] == server : + if DEBUG : print('->HandlerKey: {0}'.format(handlerkey[1])) + + handleropnames.append(handlerkey[1]) + + if len(handleropnames) == 0 : return False + else : handleropnames.sort() # sort to process opcode names in ascending order + + for client in clients : + clientopnames = [] + clientencodes = [] + clientdecodes = [] + + notranslation = 0 + encodeonly = 0 + decodeonly = 0 + encodedecode = 0 + totalopcodes = 0 + + for clientopkey in clientops.keys() : + if clientopkey[0] == client : + if DEBUG : print('->ClientOpKey: {0}'.format(clientopkey[1])) + + clientopnames.append(clientopkey[1]) + + if len(clientopnames) == 0 : return False + + for encodeentry in encodes : + if encodeentry[0] == client : + if DEBUG : print('->EncodeEntry: {0}'.format(encodeentry[1])) + + clientencodes.append(encodeentry[1]) + + if len(clientencodes) == 0 : return False + + for decodeentry in decodes : + if decodeentry[0] == client : + if DEBUG : print('->DecodeEntry: {0}'.format(decodeentry[1])) + + clientdecodes.append(decodeentry[1]) + + if len(clientdecodes) == 0 : return False + + for handleropentry in handleropnames : + try : clientopindex = clientopnames.index(handleropentry) + except : clientopindex = -1 + + if clientopindex > -1 : + val0 = clientopnames[clientopindex] + val1 = serverops[val0] + val2 = clientops[(client, val0)] + + if DEBUG : print('->Opcode: {0} ({1}: {2} | {3}: {4})'.format(val0, server, val1, client, val2)) + + outstreams[client].write('Opcode: {0} | {1}: {2} | {3}: {4}\n'.format(val0, server, val1, client, val2)) + + val3 = handlers[(server, val0)] + + if DEBUG : print('->{0} Handler: {1}'.format(server, val3)) + + outstreams[client].write(' {0} Handler: {1}\n'.format(server, val3)) + + try : val4 = clientencodes.index(val0) > -1 + except : val4 = False + try : val5 = clientdecodes.index(val0) > -1 + except : val5 = False + + if DEBUG : print('Encoded: {0} | Decoded: {1}'.format(val4, val5)) + + outstreams[client].write(' Encoded: {0} | Decoded: {1}\n\n'.format(val4, val5)) + + totalopcodes = totalopcodes + 1 + + if val4 and val5 : encodedecode = encodedecode + 1 + elif val4 and not val5 : encodeonly = encodeonly + 1 + elif not val4 and val5 : decodeonly = decodeonly + 1 + elif not val4 and not val5 : notranslation = notranslation + 1 + + if DEBUG : print('->EndOfOpcodeLoop: {0}'.format(val0)) + + if DEBUG : print('->OpcodeCount: {0}'.format(totalopcodes)) + if DEBUG : print('->Translations: (Bi-directional: {0}, EncodeOnly: {1}, DecodeOnly: {2}, NoTranslation: {3})'.format(encodedecode, encodeonly, decodeonly, notranslation)) + + outstreams[client].write('Statistics *******************************************\n') + outstreams[client].write('** Handled Opcodes: {0}\n'.format(totalopcodes)) + outstreams[client].write('** Bi-directional Translations: {0}\n'.format(encodeonly)) + outstreams[client].write('** Encodes Only: {0}\n'.format(encodeonly)) + outstreams[client].write('** Decodes Only: {0}\n'.format(decodeonly)) + outstreams[client].write('** No Translations: {0}\n'.format(notranslation)) + outstreams[client].write('Notes ************************************************\n') + outstreams[client].write('** \'Bi-directional\' indicates translations are performed on tx/rx packets\n') + outstreams[client].write('** \'Encodes Only\' indicates translations only on tx packets - does not exclude direct packet rx\n') + outstreams[client].write('** \'Decodes Only\' indicates translations only on rx packets - does not exclude direct packet tx\n') + outstreams[client].write('** \'No Translations\' indicates no translations on tx/rx of packets\n') + + if DEBUG : print('->EndOfClientLoop: {0}'.format(client)) + + if DEBUG : print('->EndOfServerLoop: {0}'.format(server)) + + return True + + +def DestroyOutputStreams() : + for client in clients : + if client in outstreams.keys() : + outstreams[client].close() + del outstreams[client] + + return True + + +if __name__ == '__main__': + main() From 32243aa3837b1e5c9e732450a8d738a7a347e3a4 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 4 Aug 2014 23:12:28 -0400 Subject: [PATCH 09/22] Unix fix... Thanks Demonstar!! --- utils/scripts/opcode_handlers.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index cae5fdc5c..0a9c63bd0 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -79,7 +79,7 @@ def main() : def LoadServerOpcodes() : # Server opcodes are auto-enumerated with a starting reference of '1' try : - filename = '{0}{1}{2}'.format(repopath, '\common', '\emu_oplist.h') + filename = '{0}{1}{2}'.format(repopath, '/common', '/emu_oplist.h') if VERBOSE or DEBUG : print(filename) @@ -130,7 +130,7 @@ def LoadClientOpcodes() : for client in clients : try : - filename = '{0}{1}{2}{3}{4}'.format(repopath, '\utils\patches', '\patch_', client, '.conf') + filename = '{0}{1}{2}{3}{4}'.format(repopath, '/utils/patches', '/patch_', client, '.conf') if VERBOSE or DEBUG : print(filename) @@ -192,10 +192,10 @@ def LoadClientOpcodes() : def LoadClientTranslators() : for client in clients : try : - if client == '6.2' : shortname = '{0}'.format('\Client62_ops.h') - else : shortname = '{0}{1}{2}'.format(chr(92), client, '_ops.h') + if client == '6.2' : shortname = '{0}'.format('/Client62_ops.h') + else : shortname = '{0}{1}{2}'.format('/', client, '_ops.h') - filename = '{0}{1}{2}'.format(repopath, '\common\patches', shortname) + filename = '{0}{1}{2}'.format(repopath, '/common/patches', shortname) if VERBOSE or DEBUG : print(filename) @@ -247,7 +247,7 @@ def LoadClientTranslators() : def LoadServerHandlers() : # TODO: handle remarked out definitions; add other servers, if possible try : - filename = '{0}{1}{2}'.format(repopath, '\zone', '\client_packet.cpp') + filename = '{0}{1}{2}'.format(repopath, '/zone', '/client_packet.cpp') if VERBOSE or DEBUG : print(filename) @@ -304,7 +304,7 @@ def LoadServerHandlers() : def CreateOutputDirectory() : try : - outputpath = '{0}{1}'.format(repopath, '\utils\scripts\opcode_handlers_output') + outputpath = '{0}{1}'.format(repopath, '/utils/scripts/opcode_handlers_output') if VERBOSE or DEBUG : print(outputpath) @@ -318,7 +318,7 @@ def CreateOutputDirectory() : def CreateOutputStreams() : try : for client in clients : - filename = '{0}{1}{2}{3}{4}'.format(repopath, '\utils\scripts\opcode_handlers_output', chr(92), client, '_opcode_handlers.txt') + filename = '{0}{1}{2}{3}'.format(repopath, '/utils/scripts/opcode_handlers_output/', client, '_opcode_handlers.txt') if VERBOSE or DEBUG : print(filename) From 9561f841c4fb0b67fb7fa6129d767bacd70b14b1 Mon Sep 17 00:00:00 2001 From: Uleat Date: Tue, 5 Aug 2014 16:34:57 -0400 Subject: [PATCH 10/22] Tweaked some procedural code and re-worked client opcode reader to standardize hex format (i.e., {0x0abc, 0x0ABC, 0xabc} = 0x0abc) --- utils/scripts/opcode_handlers.py | 706 +++++++++++++++++++------------ 1 file changed, 429 insertions(+), 277 deletions(-) diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index 0a9c63bd0..a677142fa 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -1,12 +1,12 @@ #! /usr/bin/env python -# untested on Linux - -# This script generates cross-references to show associated (handled) opcodes between the server and client. -# It will generate files for each client found and provide some basic information..such as opcode names and -# values, server handler and whether opcodes are translated on tx/rx. # -# It's currently limited to the 'Zone' server..but, can be expounded upon to include other servers, clients -# and other criteria and features. +# This script generates cross-references to show associated (handled) opcodes +# between the server and client. It will generate files for each client found +# and provide some basic information..such as opcode names and values, server +# handler and whether opcodes are translated on tx/rx. +# +# It's currently limited to the 'Zone' server..but, can be expounded upon to +# include other servers, clients and other criteria and features. import os @@ -15,14 +15,16 @@ import os from time import time, ctime -VERBOSE = True # pipes relativistic information to the console window -DEBUG = False # setting this to 'True' will pipe more information to the console window than you would ever want! +# pipes relativistic information to the console window +VERBOSE = True -repopath = os.getcwd() -repopath = repopath[:-14] # '\utils\scripts' - because I'm lazy and don't want to do it the right way... +# setting this to 'True' will pipe more information than you would ever want! +DEBUG = False -clients = [ '6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF' ] -servers = [ 'Login', 'World', 'Zone' ] +repopath = os.getcwd()[:-14] # '/utils/scripts' + +clients = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF'] +servers = ['Login', 'World', 'Zone', 'UCS'] clientops = {} serverops = {} @@ -34,439 +36,589 @@ handlers = {} outstreams = {} -def main() : +def main(): print('Loading source data...') - if not LoadServerOpcodes() : + if not loadserveropcodes(): print('Error! Could not load server opcodes') return - if not LoadClientOpcodes() : + if not loadclientopcodes(): print('Error! Could not load client opcodes') return - if not LoadClientTranslators() : + if not loadclienttranslators(): print('Error! Could not load client translators') return - if not LoadServerHandlers() : + if not loadserverhandlers(): print('Error! Could not load server handlers') return print('Creating output streams...') - if not CreateOutputDirectory() : - print('Output directory already exists or could not be created...') - #return # existing directory returns a failure..don't exit here..CreateOutputStreams() will catch makdir() problems + if not createoutputdirectory(): + print('Output directory could not be created...') + return - if not CreateOutputStreams() : + if not createoutputstreams(): print('Error! Could not open output files') return print('Parsing opcode data...') - if not ParseOpcodeData() : + if not parseopcodedata(): print('Error! Could not parse opcode data') return print('Destroying output streams...') - if not DestroyOutputStreams() : + if not destroyoutputstreams(): print('Error! Could not close output files') return -def LoadServerOpcodes() : +def loadserveropcodes(): # Server opcodes are auto-enumerated with a starting reference of '1' - try : - filename = '{0}{1}{2}'.format(repopath, '/common', '/emu_oplist.h') + try: + value = 0 + + filename = '{0}{1}{2}'.format( + repopath, + '/common', + '/emu_oplist.h') - if VERBOSE or DEBUG : print(filename) - - with open(filename, 'r') as datafile : - value = 0 - serverops['Null'] = value - - while True : - begin = 0 - end = 0 - + if VERBOSE or DEBUG: + print(filename) + + with open(filename, 'r') as datafile: + while True: dataline = datafile.readline() - if not dataline : break - if not dataline[:1] == 'N' : continue - - while not dataline[begin:(begin + 1)] == '(' : - if begin >= len(dataline) : break - else : begin = begin + 1 + if not dataline: + break - end = begin + 1 + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue + + value += 1 - while not dataline[(end - 1):end] == ')' : - if end >= len(dataline) : break - else : end = end + 1 + if dataline[:1] == 'N': + serverops[dataline[vbegin:vend]] = value - begin = begin + 1 # adjust out the '(' - end = end - 1 # adjust out the ')' - - if begin >= end or begin >= len(dataline) : continue - if end >= len(dataline) : continue - - value = value + 1 - - serverops[dataline[begin:end]] = value - - if DEBUG : print('({0}, {1}) = {2}'.format('Server', dataline[begin:end], serverops[dataline[begin:end]])) + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Server', + dataline[vbegin:vend], + serverops[dataline[vbegin:vend]])) datafile.close() + + filename = '{0}{1}{2}'.format( + repopath, + '/common', + '/mail_oplist.h') + + if VERBOSE or DEBUG: + print(filename) + + with open(filename, 'r') as datafile: + while True: + dataline = datafile.readline() - except : return False + if not dataline: + break + + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue + + value += 1 + + if dataline[:1] == 'N': + serverops[dataline[vbegin:vend]] = value + + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Server', + dataline[vbegin:vend], + serverops[dataline[vbegin:vend]])) + + datafile.close() + except: + return False return True -def LoadClientOpcodes() : +def loadclientopcodes(): badclients = [] - for client in clients : - try : - filename = '{0}{1}{2}{3}{4}'.format(repopath, '/utils/patches', '/patch_', client, '.conf') + for client in clients: + try: + filename = '{0}{1}{2}{3}{4}'.format( + repopath, + '/utils/patches', + '/patch_', + client, + '.conf') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : - while True : - kbegin = 0 - vbegin = 0 - kend = 0 - vend = 0 - + with open(filename, 'r') as datafile: + while True: dataline = datafile.readline() - if not dataline : break - if not dataline[0:3] == 'OP_' : continue + if not dataline: + break - kend = kbegin + 3 + kbegin = dataline.find('OP_') + kend = dataline.find('=', kbegin) - while not dataline[(kend - 1):kend] == '=' : - if kend >= len(dataline) : break - else : kend = kend + 1 - - kend = kend - 1 # adjust out the '=' - vbegin = kend + 1 + if not kbegin == 0: + continue + if kend == -1: + continue + + vbegin = dataline.find('0x', kend) vend = vbegin + 6 - if dataline[vbegin:vend] == '0x0000' : continue + if vbegin == -1: + continue + + value = int(dataline[(vbegin + 2):vend].lower(), 16) + + if value == 0: + continue + + clientops[client, dataline[kbegin:kend]] = '0x{0}'.format( + hex(value)[2:].zfill(4)) - clientops[client, dataline[kbegin:kend]] = dataline[vbegin:vend] - - if DEBUG : print('({0}, {1}) = {2}'.format(client, dataline[kbegin:kend], clientops[client, dataline[kbegin:kend]])) + if DEBUG: + print('({0}, {1}) = {2} (int: {3})'.format( + client, + dataline[kbegin:kend], + clientops[client, dataline[kbegin:kend]], + value)) datafile.close() + except: + badclients.append(client) - except : badclients.append(client) - - if len(badclients) > 0 : + if len(badclients) > 0: badentries = [] - for badclient in badclients : - if VERBOSE or DEBUG : print('Deleting client {0} from search criteria...'.format(badclient)) + for badclient in badclients: + if VERBOSE or DEBUG: + print('Deleting client {0} from search criteria...'.format( + badclient)) clients.remove(badclient) - if VERBOSE or DEBUG : print('Deleting any partial entries for client {0}...'.format(badclient)) + if VERBOSE or DEBUG: + print('Deleting any partial entries for client {0}...'.format( + badclient)) - for entry in clientops.keys() : - if entry[0] == client : + for entry in clientops.keys(): + if entry[0] == badclient: badentries.append(entry) - for badentry in badentries : + for badentry in badentries: del clientops[badentry] - if len(clients) == 0 : + if len(clients) == 0: return False return True -def LoadClientTranslators() : - for client in clients : - try : - if client == '6.2' : shortname = '{0}'.format('/Client62_ops.h') - else : shortname = '{0}{1}{2}'.format('/', client, '_ops.h') - - filename = '{0}{1}{2}'.format(repopath, '/common/patches', shortname) +def loadclienttranslators(): + for client in clients: + if client == '6.2': + shortname = '{0}'.format( + '/Client62_ops.h') + else: + shortname = '{0}{1}{2}'.format( + '/', + client, + '_ops.h') + + try: + filename = '{0}{1}{2}'.format( + repopath, + '/common/patches', + shortname) - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : + with open(filename, 'r') as datafile: while True: - begin = 0 - end = 0 - dataline = datafile.readline() - if not dataline : break - if not dataline[:1] == 'E' and not dataline[0:1] == 'D' : continue - - while not dataline[begin:(begin + 1)] == '(' : - if begin >= len(dataline) : break - else : begin = begin + 1 + if not dataline: + break - end = begin + 1 - - while not dataline[(end - 1):end] == ')' : - if end >= len(dataline) : break - else : end = end + 1 - - begin = begin + 1 # adjust out the '(' - end = end - 1 # adjust out the ')' - - if begin >= end or begin >= len(dataline) : continue - if end >= len(dataline) : continue + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue - if dataline[:1] == 'E' : - encodes.append((client, dataline[begin:end])) + if dataline[:1] == 'E': + encodes.append((client, dataline[vbegin:vend])) - if DEBUG : print('ENCODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], encodes.index((client, dataline[begin:end])))) - elif dataline[:1] == 'D' : - decodes.append((client, dataline[begin:end])) + if DEBUG: + print('ENCODE({0}, {1}) [floating index: {2}]'.format( + client, + dataline[vbegin:vend], + encodes.index((client, dataline[vbegin:vend])))) + elif dataline[:1] == 'D': + decodes.append((client, dataline[vbegin:vend])) - if DEBUG : print('DECODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], decodes.index((client, dataline[begin:end])))) + if DEBUG: + print('DECODE({0}, {1}) [floating index: {2}]'.format( + client, + dataline[vbegin:vend], + decodes.index((client, dataline[vbegin:vend])))) datafile.close() + except: + # TODO: need to handle + return False - except : return False # TODO: need to handle - - if len(encodes) == 0 and len(decodes) == 0 : + # this will need to change if we ever have a client with 100% support + if len(encodes) == 0 and len(decodes) == 0: return False return True -def LoadServerHandlers() : +def loadserverhandlers(): # TODO: handle remarked out definitions; add other servers, if possible - try : - filename = '{0}{1}{2}'.format(repopath, '/zone', '/client_packet.cpp') + try: + filename = '{0}{1}{2}'.format( + repopath, + '/zone', + '/client_packet.cpp') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : + with open(filename, 'r') as datafile: dataline = datafile.readline() - while not dataline[:19] == 'void MapOpcodes() {' : + while not dataline[:19] == 'void MapOpcodes() {': dataline = datafile.readline() - if not dataline : break + if not dataline: + break - while True : - kbegin = 0 - vbegin = 0 - kend = 0 - vend = 0 - + while True: dataline = datafile.readline() - if not dataline : break - if dataline[0:1] == '}' : break + if not dataline: + break + if dataline[0:1] == '}': + break kbegin = dataline.find('OP_') + kend = dataline.find(']', kbegin) - if kbegin == -1 : continue + if kbegin == -1: + continue + if kend == -1: + continue - kend = kbegin + 1 - - while not dataline[kend:(kend + 1)] == ']' : - if kend >= len(dataline) : break - else : kend = kend + 1 + vbegin = dataline.find('Client::', kend) + vend = dataline.find(';', vbegin) - vbegin = dataline.find('&Client::') - - if vbegin == -1 : continue - - vbegin = vbegin + 1 - vend = vbegin + 9 - - while not dataline[vend:(vend + 1)] == ';' : - if vend >= len(dataline) : break - else : vend = vend + 1 + if vbegin == -1: + continue + if vend == -1: + continue handlers['Zone', dataline[kbegin:kend]] = dataline[vbegin:vend] - if DEBUG : print('({0}, {1}) = {2}'.format('Zone', dataline[kbegin:kend], handlers['Zone', dataline[kbegin:kend]])) + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Zone', + dataline[kbegin:kend], + handlers['Zone', dataline[kbegin:kend]])) datafile.close() - - except : return False # should probably do the same for this (badservers) as done for badclients above + except: + # should probably do the same for this (badservers) + # as done for badclients in the above function + return False return True -def CreateOutputDirectory() : - try : - outputpath = '{0}{1}'.format(repopath, '/utils/scripts/opcode_handlers_output') +def createoutputdirectory(): + outputpath = '' + + try: + outputpath = '{0}{1}'.format( + repopath, + '/utils/scripts/opcode_handlers_output') - if VERBOSE or DEBUG : print(outputpath) - - os.mkdir(outputpath) + if VERBOSE or DEBUG: + print(outputpath) + + if not os.path.exists(outputpath): + os.mkdir(outputpath) return True + except: + return False + + +def createoutputstreams(): + filename = '' - except : return False - - -def CreateOutputStreams() : - try : - for client in clients : - filename = '{0}{1}{2}{3}'.format(repopath, '/utils/scripts/opcode_handlers_output/', client, '_opcode_handlers.txt') + try: + for client in clients: + filename = '{0}{1}{2}{3}'.format( + repopath, + '/utils/scripts/opcode_handlers_output/', + client, + '_opcode_handlers.txt') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) outstreams[client] = open(filename, 'w') - - outstreams[client].write('******************************************************\n') - outstreams[client].write('** Opcode-Handler analysis for \'{0}\' client\n'.format(client)) - outstreams[client].write('** script-generated file @ {0}\n'.format(ctime(time()))) - outstreams[client].write('**\n') - outstreams[client].write('** (only cross-linked (active) opcodes are listed)\n') - outstreams[client].write('******************************************************\n\n') - - except : - for client in clients : - if client in outstreams.keys() : + + message = \ + '------------------------------------------------------\n' \ + '|| Opcode-Handler analysis for \'{0}\' client\n' \ + '|| script-generated file @ {1}\n' \ + '||\n' \ + '|| (only cross-linked (active) opcodes are listed)\n' \ + '------------------------------------------------------\n' \ + '\n'.format( + client, + ctime(time())) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-2]) + except: + for client in clients: + if client in outstreams.keys(): outstreams[client].close() del outstreams[client] + + if DEBUG: + print('->CreatingClientStream(exception): {0}'.format( + client)) return False return True -def ParseOpcodeData() : +def parseopcodedata(): serveropnames = [] - for serveropkey in serverops.keys() : - if serveropkey == 'Null' : continue - - if DEBUG : print('->ServerOpKey: {0}'.format(serveropkey)) - + for serveropkey in serverops.keys(): serveropnames.append(serveropkey) - if len(serveropnames) == 0 : return False + if DEBUG: + print('->ServerOpKey: {0}'.format( + serveropkey)) + + if len(serveropnames) == 0: + return False - for server in servers : - if server == 'Login' or server == 'World' : continue # Login, World not implemented yet + for server in servers: + # Login, World, UCS not implemented yet + if not server == 'Zone': + if DEBUG: + print('->SkippingServerOpcodeParse: {0}'.format( + server)) + + continue handleropnames = [] - for handlerkey in handlers.keys() : - if handlerkey[0] == server : - if DEBUG : print('->HandlerKey: {0}'.format(handlerkey[1])) - + for handlerkey in handlers.keys(): + if handlerkey[0] == server: handleropnames.append(handlerkey[1]) + + if DEBUG: + print('->HandlerKey: {0}'.format( + handlerkey[1])) - if len(handleropnames) == 0 : return False - else : handleropnames.sort() # sort to process opcode names in ascending order + if len(handleropnames) == 0: + return False + else: + # sort to process opcode names in ascending order + handleropnames.sort() - for client in clients : + for client in clients: clientopnames = [] clientencodes = [] clientdecodes = [] - notranslation = 0 + totalopcodes = 0 + encodedecode = 0 encodeonly = 0 decodeonly = 0 - encodedecode = 0 - totalopcodes = 0 + notranslation = 0 - for clientopkey in clientops.keys() : - if clientopkey[0] == client : - if DEBUG : print('->ClientOpKey: {0}'.format(clientopkey[1])) - + for clientopkey in clientops.keys(): + if clientopkey[0] == client: clientopnames.append(clientopkey[1]) - - if len(clientopnames) == 0 : return False - - for encodeentry in encodes : - if encodeentry[0] == client : - if DEBUG : print('->EncodeEntry: {0}'.format(encodeentry[1])) + if DEBUG: + print('->ClientOpKey: {0}'.format( + clientopkey[1])) + + if len(clientopnames) == 0: + return False + + for encodeentry in encodes: + if encodeentry[0] == client: clientencodes.append(encodeentry[1]) - - if len(clientencodes) == 0 : return False - - for decodeentry in decodes : - if decodeentry[0] == client : - if DEBUG : print('->DecodeEntry: {0}'.format(decodeentry[1])) - clientdecodes.append(decodeentry[1]) + if DEBUG: + print('->EncodeEntry: {0}'.format( + encodeentry[1])) - if len(clientdecodes) == 0 : return False + if len(clientencodes) == 0: + return False + + for decodeentry in decodes: + if decodeentry[0] == client: + clientdecodes.append(decodeentry[1]) + + if DEBUG: + print('->DecodeEntry: {0}'.format( + decodeentry[1])) + + if len(clientdecodes) == 0: + return False - for handleropentry in handleropnames : - try : clientopindex = clientopnames.index(handleropentry) - except : clientopindex = -1 + for handleropentry in handleropnames: + try: + clientopindex = clientopnames.index(handleropentry) + except: + clientopindex = -1 - if clientopindex > -1 : + if clientopindex > -1: val0 = clientopnames[clientopindex] val1 = serverops[val0] val2 = clientops[(client, val0)] - - if DEBUG : print('->Opcode: {0} ({1}: {2} | {3}: {4})'.format(val0, server, val1, client, val2)) - - outstreams[client].write('Opcode: {0} | {1}: {2} | {3}: {4}\n'.format(val0, server, val1, client, val2)) - val3 = handlers[(server, val0)] - if DEBUG : print('->{0} Handler: {1}'.format(server, val3)) + try: + val4 = clientencodes.index(val0) > -1 + except: + val4 = False + try: + val5 = clientdecodes.index(val0) > -1 + except: + val5 = False - outstreams[client].write(' {0} Handler: {1}\n'.format(server, val3)) + message = \ + 'Opcode: {0} | {1}: {2} | {3}: {4}\n' \ + ' {5} Handler: \'{6}\'\n' \ + ' Encoded: {7} | Decoded: {8}\n\n'.format( + val0, + server, + val1, + client, + val2, + server, + val3, + val4, + val5) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-2]) + + totalopcodes += 1 + + if val4 and val5: + encodedecode += 1 + elif val4 and not val5: + encodeonly += 1 + elif not val4 and val5: + decodeonly += 1 + elif not val4 and not val5: + notranslation += 1 - try : val4 = clientencodes.index(val0) > -1 - except : val4 = False - try : val5 = clientdecodes.index(val0) > -1 - except : val5 = False + if DEBUG: + print('->EndOfOpcodeLoop: {0}'.format( + val0)) + + message = \ + 'Statistics -------------------------------------------\n' \ + '|| Server Opcode Type: {0}\n' \ + '|| Handled Opcodes: {1}\n' \ + '|| Bi-directional: {2}\n' \ + '|| Encodes Only: {3}\n' \ + '|| Decodes Only: {4}\n' \ + '|| No Translations: {5}\n' \ + '\n' \ + 'Notes ------------------------------------------------\n' \ + '|| Encodes are Server-to-Client and Decodes are' \ + ' Server-from-Client in context\n' \ + '|| \'Bi-directional\' indicates translations are performed on' \ + ' tx/rx packets\n' \ + '|| \'Encodes Only\' indicates translations only on tx packets' \ + ' - does not exclude direct packet rx\n' \ + '|| \'Decodes Only\' indicates translations only on rx packets' \ + ' - does not exclude direct packet tx\n' \ + '|| \'No Translations\' indicates no translations of tx/rx' \ + ' packets\n'.format( + server, + totalopcodes, + encodedecode, + encodeonly, + decodeonly, + notranslation) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-1]) + print('->EndOfClientLoop: {0}'.format( + client)) - if DEBUG : print('Encoded: {0} | Decoded: {1}'.format(val4, val5)) - - outstreams[client].write(' Encoded: {0} | Decoded: {1}\n\n'.format(val4, val5)) - - totalopcodes = totalopcodes + 1 - - if val4 and val5 : encodedecode = encodedecode + 1 - elif val4 and not val5 : encodeonly = encodeonly + 1 - elif not val4 and val5 : decodeonly = decodeonly + 1 - elif not val4 and not val5 : notranslation = notranslation + 1 - - if DEBUG : print('->EndOfOpcodeLoop: {0}'.format(val0)) - - if DEBUG : print('->OpcodeCount: {0}'.format(totalopcodes)) - if DEBUG : print('->Translations: (Bi-directional: {0}, EncodeOnly: {1}, DecodeOnly: {2}, NoTranslation: {3})'.format(encodedecode, encodeonly, decodeonly, notranslation)) - - outstreams[client].write('Statistics *******************************************\n') - outstreams[client].write('** Handled Opcodes: {0}\n'.format(totalopcodes)) - outstreams[client].write('** Bi-directional Translations: {0}\n'.format(encodeonly)) - outstreams[client].write('** Encodes Only: {0}\n'.format(encodeonly)) - outstreams[client].write('** Decodes Only: {0}\n'.format(decodeonly)) - outstreams[client].write('** No Translations: {0}\n'.format(notranslation)) - outstreams[client].write('Notes ************************************************\n') - outstreams[client].write('** \'Bi-directional\' indicates translations are performed on tx/rx packets\n') - outstreams[client].write('** \'Encodes Only\' indicates translations only on tx packets - does not exclude direct packet rx\n') - outstreams[client].write('** \'Decodes Only\' indicates translations only on rx packets - does not exclude direct packet tx\n') - outstreams[client].write('** \'No Translations\' indicates no translations on tx/rx of packets\n') - - if DEBUG : print('->EndOfClientLoop: {0}'.format(client)) - - if DEBUG : print('->EndOfServerLoop: {0}'.format(server)) + if DEBUG: + print('->EndOfServerLoop: {0}'.format( + server)) return True -def DestroyOutputStreams() : - for client in clients : - if client in outstreams.keys() : +def destroyoutputstreams(): + for client in clients: + if client in outstreams.keys(): outstreams[client].close() del outstreams[client] + + if DEBUG: + print('->DestroyingClientStream: {0}'.format( + client)) return True From aab3cac29c493ee7f831f89a7da27a0219fc0011 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 6 Aug 2014 06:49:29 -0400 Subject: [PATCH 11/22] fix --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 078827b78..9f6a3866b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -6468,6 +6468,6 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); mod *= 100.0f; - SetSpellPSetSpellPowerDistanceMod(static_cast(mod)); + SetSpellPowerDistanceMod(static_cast(mod)); } } \ No newline at end of file From f0496a6f59f5dbf58ae210a1c455c0580ddf2575 Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 7 Aug 2014 15:27:30 -0400 Subject: [PATCH 12/22] Total re-work..not much more can be done until more server handlers are added... --- utils/scripts/opcode_handlers.py | 978 ++++++++++++++++--------------- 1 file changed, 505 insertions(+), 473 deletions(-) diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index a677142fa..ca4a00d3d 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -1,625 +1,657 @@ #! /usr/bin/env python # # This script generates cross-references to show associated (handled) opcodes -# between the server and client. It will generate files for each client found -# and provide some basic information..such as opcode names and values, server -# handler and whether opcodes are translated on tx/rx. +# between the server and client. It will generate files for each client and +# server found, and provide some basic information..such as opcode names and +# values, server handler and whether opcodes are translated on tx/rx, etc... # # It's currently limited to the 'Zone' server..but, can be expounded upon to -# include other servers, clients and other criteria and features. +# include other servers and clients, and other criteria and features. import os -#import pickle +# import pickle from time import time, ctime -# pipes relativistic information to the console window -VERBOSE = True +DEBUG = 1 # {0 - normal, 1 - verbose, 2 - in-depth} -# setting this to 'True' will pipe more information than you would ever want! -DEBUG = False +base_path = os.getcwd()[:-14] # '/utils/scripts' -repopath = os.getcwd()[:-14] # '/utils/scripts' +client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF'] +server_list = ['Login', 'World', 'Zone', 'UCS'] -clients = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF'] -servers = ['Login', 'World', 'Zone', 'UCS'] +client_opcodes = {} +server_opcodes = {} -clientops = {} -serverops = {} +client_encodes = {} +client_decodes = {} -encodes = [] -decodes = [] +server_handlers = {} -handlers = {} -outstreams = {} +out_files = {} def main(): print('Loading source data...') - if not loadserveropcodes(): - print('Error! Could not load server opcodes') - return - if not loadclientopcodes(): - print('Error! Could not load client opcodes') + print('Could not load client opcodes...') return - + + if not loadserveropcodes(): + print('Could not load server opcodes...') + return + if not loadclienttranslators(): - print('Error! Could not load client translators') + print('Could not load client translators...') return if not loadserverhandlers(): - print('Error! Could not load server handlers') + print('Could not load server handlers...') return print('Creating output streams...') if not createoutputdirectory(): - print('Output directory could not be created...') + print('Could not create output directory...') return - if not createoutputstreams(): - print('Error! Could not open output files') + if not openoutputfiles(): + print('Could not open output files...') return print('Parsing opcode data...') - if not parseopcodedata(): - print('Error! Could not parse opcode data') + if not parseclientopcodedata(): + print('Could not parse client opcode data...') + return + + if not parseserveropcodedata(): + print('Could not parse server opcode data...') return print('Destroying output streams...') - if not destroyoutputstreams(): - print('Error! Could not close output files') + if not closeoutputfiles(): + print('Could not close output files...') return -def loadserveropcodes(): - # Server opcodes are auto-enumerated with a starting reference of '1' - try: - value = 0 - - filename = '{0}{1}{2}'.format( - repopath, - '/common', - '/emu_oplist.h') - - if VERBOSE or DEBUG: - print(filename) - - with open(filename, 'r') as datafile: - while True: - dataline = datafile.readline() - - if not dataline: - break - - vbegin = dataline.find('OP_', 2) - vend = dataline.find(')', vbegin) - - if vbegin == -1: - continue - if vend == -1: - continue - - value += 1 - - if dataline[:1] == 'N': - serverops[dataline[vbegin:vend]] = value - - if DEBUG: - print('({0}, {1}) = {2}'.format( - 'Server', - dataline[vbegin:vend], - serverops[dataline[vbegin:vend]])) - - datafile.close() - - filename = '{0}{1}{2}'.format( - repopath, - '/common', - '/mail_oplist.h') - - if VERBOSE or DEBUG: - print(filename) - - with open(filename, 'r') as datafile: - while True: - dataline = datafile.readline() - - if not dataline: - break - - vbegin = dataline.find('OP_', 2) - vend = dataline.find(')', vbegin) - - if vbegin == -1: - continue - if vend == -1: - continue - - value += 1 - - if dataline[:1] == 'N': - serverops[dataline[vbegin:vend]] = value - - if DEBUG: - print('({0}, {1}) = {2}'.format( - 'Server', - dataline[vbegin:vend], - serverops[dataline[vbegin:vend]])) - - datafile.close() - except: - return False - - return True - - def loadclientopcodes(): - badclients = [] + bad_clients = [] - for client in clients: + for client in client_list: try: - filename = '{0}{1}{2}{3}{4}'.format( - repopath, - '/utils/patches', + short_name = '{0}{1}{2}'.format( '/patch_', client, '.conf') - - if VERBOSE or DEBUG: - print(filename) - with open(filename, 'r') as datafile: + file_name = '{0}{1}{2}'.format( + base_path, + '/utils/patches', + short_name) + + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + client_opcodes[client] = {} # force empty dictionary to avoid collisions + while True: - dataline = datafile.readline() - - if not dataline: + data_line = data_file.readline() + + if not data_line: break - kbegin = dataline.find('OP_') - kend = dataline.find('=', kbegin) + key_begin = data_line.find('OP_') + key_end = data_line.find('=', key_begin) - if not kbegin == 0: - continue - if kend == -1: - continue - - vbegin = dataline.find('0x', kend) - vend = vbegin + 6 - - if vbegin == -1: + if not key_begin == 0 or key_end < 0: continue - value = int(dataline[(vbegin + 2):vend].lower(), 16) + val_begin = data_line.find('0x', key_end) + val_end = val_begin + 6 # max size is always 6 bytes + + if val_begin < 0: + continue + + value = int(data_line[(val_begin + 2):val_end].lower(), 16) if value == 0: continue - clientops[client, dataline[kbegin:kend]] = '0x{0}'.format( - hex(value)[2:].zfill(4)) - - if DEBUG: - print('({0}, {1}) = {2} (int: {3})'.format( + client_opcodes[client][data_line[key_begin:key_end]] = '0x{0}'.format(hex(value)[2:].zfill(4)) + + if DEBUG >= 2: + print('[{0}][{1}] = {2} (int: {3})'.format( client, - dataline[kbegin:kend], - clientops[client, dataline[kbegin:kend]], + data_line[key_begin:key_end], + client_opcodes[client][data_line[key_begin:key_end]], value)) - datafile.close() - except: - badclients.append(client) + data_file.close() + except: # StandardError as se: + #print(se.message) - if len(badclients) > 0: - badentries = [] - - for badclient in badclients: - if VERBOSE or DEBUG: - print('Deleting client {0} from search criteria...'.format( - badclient)) - - clients.remove(badclient) + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) - if VERBOSE or DEBUG: - print('Deleting any partial entries for client {0}...'.format( - badclient)) + bad_clients.append(client) + + if len(bad_clients) > 0: + for bad_client in bad_clients: + if DEBUG >= 1: + print('Deleting \'{0}\' client from search criteria...'.format(bad_client)) - for entry in clientops.keys(): - if entry[0] == badclient: - badentries.append(entry) - - for badentry in badentries: - del clientops[badentry] + client_list.remove(bad_client) + + if DEBUG >= 1: + print('Deleting stale entries for \'{0}\' client...'.format(bad_client)) + + if bad_client in client_opcodes: + del client_opcodes[bad_client] - if len(clients) == 0: + if not len(client_list) > 0: + return False + + return True + + +def loadserveropcodes(): + try: + value = 1 # Server opcodes are auto-enumerated with a starting reference of '1' + + file_name = '{0}{1}{2}'.format( + base_path, + '/common', + '/emu_oplist.h') + + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + while True: + data_line = data_file.readline() + + if not data_line: + break + + val_begin = data_line.find('OP_', 2) + val_end = data_line.find(')', val_begin) + + if val_begin < 0 or val_end < 0: + continue + + if data_line[:1] == 'N': + server_opcodes[data_line[val_begin:val_end]] = value + value += 1 + + if DEBUG >= 2: + print('N[{0}]({1}) = {2}'.format( + 'Server', + data_line[val_begin:val_end], + server_opcodes[data_line[val_begin:val_end]])) + + data_file.close() + + file_name = '{0}{1}{2}'.format( + base_path, + '/common', + '/mail_oplist.h') + + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + while True: + data_line = data_file.readline() + + if not data_line: + break + + val_begin = data_line.find('OP_', 2) + val_end = data_line.find(')', val_begin) + + if val_begin < 0 or val_end < 0: + continue + + if data_line[:1] == 'N': + server_opcodes[data_line[val_begin:val_end]] = value + value += 1 + + if DEBUG >= 2: + print('N[{0}]({1}) = {2}'.format( + 'Server', + data_line[val_begin:val_end], + server_opcodes[data_line[val_begin:val_end]])) + + data_file.close() + except: # StandardError as se: + #print(se.message) + + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) + return False return True def loadclienttranslators(): - for client in clients: + for client in client_list: if client == '6.2': - shortname = '{0}'.format( - '/Client62_ops.h') + short_name = '{0}'.format('/Client62_ops.h') else: - shortname = '{0}{1}{2}'.format( + short_name = '{0}{1}{2}'.format( '/', client, '_ops.h') try: - filename = '{0}{1}{2}'.format( - repopath, + file_name = '{0}{1}{2}'.format( + base_path, '/common/patches', - shortname) + short_name) - if VERBOSE or DEBUG: - print(filename) + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + client_encodes[client] = [] + client_decodes[client] = [] - with open(filename, 'r') as datafile: while True: - dataline = datafile.readline() + data_line = data_file.readline() - if not dataline: + if not data_line: break - vbegin = dataline.find('OP_', 2) - vend = dataline.find(')', vbegin) + val_begin = data_line.find('OP_', 2) + val_end = data_line.find(')', val_begin) - if vbegin == -1: - continue - if vend == -1: + if val_begin < 0 or val_end < 0: continue - if dataline[:1] == 'E': - encodes.append((client, dataline[vbegin:vend])) + if data_line[:1] == 'E': + client_encodes[client].append(data_line[val_begin:val_end]) - if DEBUG: - print('ENCODE({0}, {1}) [floating index: {2}]'.format( + if DEBUG >= 2: + print('E[{0}]({1}) (listed: {2})'.format( client, - dataline[vbegin:vend], - encodes.index((client, dataline[vbegin:vend])))) - elif dataline[:1] == 'D': - decodes.append((client, dataline[vbegin:vend])) + data_line[val_begin:val_end], + data_line[val_begin:val_end] in client_encodes[client])) + elif data_line[:1] == 'D': + client_decodes[client].append(data_line[val_begin:val_end]) - if DEBUG: - print('DECODE({0}, {1}) [floating index: {2}]'.format( + if DEBUG >= 2: + print('D[{0}]({1}) (listed: {2})'.format( client, - dataline[vbegin:vend], - decodes.index((client, dataline[vbegin:vend])))) + data_line[val_begin:val_end], + data_line[val_begin:val_end] in client_decodes[client])) + + data_file.close() + except: # StandardError as se: + #print(se.message) + + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) - datafile.close() - except: - # TODO: need to handle return False - # this will need to change if we ever have a client with 100% support - if len(encodes) == 0 and len(decodes) == 0: + # there's always going to be at least one client with one encode or decode + if not len(client_encodes) > 0 and not len(client_decodes) > 0: return False return True def loadserverhandlers(): - # TODO: handle remarked out definitions; add other servers, if possible - try: - filename = '{0}{1}{2}'.format( - repopath, - '/zone', - '/client_packet.cpp') - - if VERBOSE or DEBUG: - print(filename) - - with open(filename, 'r') as datafile: - dataline = datafile.readline() - - while not dataline[:19] == 'void MapOpcodes() {': - dataline = datafile.readline() - - if not dataline: - break - - while True: - dataline = datafile.readline() - - if not dataline: - break - if dataline[0:1] == '}': - break - - kbegin = dataline.find('OP_') - kend = dataline.find(']', kbegin) - - if kbegin == -1: - continue - if kend == -1: - continue - - vbegin = dataline.find('Client::', kend) - vend = dataline.find(';', vbegin) - - if vbegin == -1: - continue - if vend == -1: - continue - - handlers['Zone', dataline[kbegin:kend]] = dataline[vbegin:vend] - - if DEBUG: - print('({0}, {1}) = {2}'.format( - 'Zone', - dataline[kbegin:kend], - handlers['Zone', dataline[kbegin:kend]])) + # TODO: handle remarked out definitions in file (i.e., // and /**/); + # TODO: out-going-only handlers need to be added..more research... + bad_servers = [] - datafile.close() - except: - # should probably do the same for this (badservers) - # as done for badclients in the above function - return False + for server in server_list: + try: + if server == 'Login': + raise + elif server == 'World': + raise + elif server == 'Zone': + file_name = '{0}{1}{2}'.format( + base_path, + '/zone', + '/client_packet.cpp') + + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + server_handlers[server] = {} + + data_line = data_file.readline() + + while not data_line[:19] == 'void MapOpcodes() {': + data_line = data_file.readline() + + if not data_line: + break + + while True: + data_line = data_file.readline() + + if not data_line or data_line[0:1] == '}': + break + + key_begin = data_line.find('OP_') + key_end = data_line.find(']', key_begin) + + if key_begin < 0 or key_end < 0: + continue + + val_begin = data_line.find('Client::', key_end) + val_end = data_line.find(';', val_begin) + + if val_begin < 0 or val_end < 0: + continue + + server_handlers[server][data_line[key_begin:key_end]] = data_line[val_begin:val_end] + + if DEBUG >= 2: + print('[{0}][{1}] = {2}'.format( + server, + data_line[key_begin:key_end], + server_handlers[server][data_line[key_begin:key_end]])) + + data_file.close() + elif server == 'UCS': + raise + else: + if DEBUG >= 2: + print('->LoadServerHandlers(Someone added a new server and forgot to code for the data load...)') + + return False + except: # StandardError as se: + #print(se.message) + + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) + + bad_servers.append(server) + + if len(bad_servers) > 0: + for bad_server in bad_servers: + if DEBUG >= 1: + print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) + + server_list.remove(bad_server) + + if DEBUG >= 1: + print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + + if bad_server in server_handlers: + del server_handlers[bad_server] + + if not len(server_list) > 0: + return False return True def createoutputdirectory(): - outputpath = '' - try: - outputpath = '{0}{1}'.format( - repopath, + output_path = '{0}{1}'.format( + base_path, '/utils/scripts/opcode_handlers_output') - if VERBOSE or DEBUG: - print(outputpath) + if DEBUG >= 1: + print(output_path) - if not os.path.exists(outputpath): - os.mkdir(outputpath) + if not os.path.exists(output_path): + os.mkdir(output_path) return True - except: + except: # StandardError as se: + #print(se.message) + + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) + return False -def createoutputstreams(): - filename = '' - +def openoutputfiles(): try: - for client in clients: - filename = '{0}{1}{2}{3}'.format( - repopath, + #file_name = '{0}{1}{2}'.format( + # base_path, + # '/utils/scripts/opcode_handlers_output/', + # 'Report.txt') + + #if DEBUG >= 1: + # print(file_name) + + #out_files['Report'] = open(file_name, 'w') + + for client in client_list: + file_name = '{0}{1}{2}{3}'.format( + base_path, '/utils/scripts/opcode_handlers_output/', client, '_opcode_handlers.txt') - if VERBOSE or DEBUG: - print(filename) + if DEBUG >= 1: + print(file_name) - outstreams[client] = open(filename, 'w') + out_files[client] = open(file_name, 'w') message = \ - '------------------------------------------------------\n' \ - '|| Opcode-Handler analysis for \'{0}\' client\n' \ - '|| script-generated file @ {1}\n' \ - '||\n' \ - '|| (only cross-linked (active) opcodes are listed)\n' \ - '------------------------------------------------------\n' \ + '>> \'Opcode-Handler\' analysis for \'{0}\' client\n' \ + '>> file generated @ {1}\n' \ '\n'.format( client, ctime(time())) - outstreams[client].write(message) + out_files[client].write(message) - if DEBUG: + if DEBUG >= 2: print(message[:-2]) - except: - for client in clients: - if client in outstreams.keys(): - outstreams[client].close() - del outstreams[client] - if DEBUG: - print('->CreatingClientStream(exception): {0}'.format( - client)) - - return False + for server in server_list: + file_name = '{0}{1}{2}{3}'.format( + base_path, + '/utils/scripts/opcode_handlers_output/', + server, + '_opcode_handlers.txt') - return True + if DEBUG >= 1: + print(file_name) - -def parseopcodedata(): - serveropnames = [] - - for serveropkey in serverops.keys(): - serveropnames.append(serveropkey) - - if DEBUG: - print('->ServerOpKey: {0}'.format( - serveropkey)) - - if len(serveropnames) == 0: - return False - - for server in servers: - # Login, World, UCS not implemented yet - if not server == 'Zone': - if DEBUG: - print('->SkippingServerOpcodeParse: {0}'.format( - server)) - - continue - - handleropnames = [] - - for handlerkey in handlers.keys(): - if handlerkey[0] == server: - handleropnames.append(handlerkey[1]) - - if DEBUG: - print('->HandlerKey: {0}'.format( - handlerkey[1])) - - if len(handleropnames) == 0: - return False - else: - # sort to process opcode names in ascending order - handleropnames.sort() - - for client in clients: - clientopnames = [] - clientencodes = [] - clientdecodes = [] - - totalopcodes = 0 - encodedecode = 0 - encodeonly = 0 - decodeonly = 0 - notranslation = 0 - - for clientopkey in clientops.keys(): - if clientopkey[0] == client: - clientopnames.append(clientopkey[1]) - - if DEBUG: - print('->ClientOpKey: {0}'.format( - clientopkey[1])) - - if len(clientopnames) == 0: - return False - - for encodeentry in encodes: - if encodeentry[0] == client: - clientencodes.append(encodeentry[1]) - - if DEBUG: - print('->EncodeEntry: {0}'.format( - encodeentry[1])) - - if len(clientencodes) == 0: - return False - - for decodeentry in decodes: - if decodeentry[0] == client: - clientdecodes.append(decodeentry[1]) - - if DEBUG: - print('->DecodeEntry: {0}'.format( - decodeentry[1])) - - if len(clientdecodes) == 0: - return False - - for handleropentry in handleropnames: - try: - clientopindex = clientopnames.index(handleropentry) - except: - clientopindex = -1 - - if clientopindex > -1: - val0 = clientopnames[clientopindex] - val1 = serverops[val0] - val2 = clientops[(client, val0)] - val3 = handlers[(server, val0)] - - try: - val4 = clientencodes.index(val0) > -1 - except: - val4 = False - try: - val5 = clientdecodes.index(val0) > -1 - except: - val5 = False - - message = \ - 'Opcode: {0} | {1}: {2} | {3}: {4}\n' \ - ' {5} Handler: \'{6}\'\n' \ - ' Encoded: {7} | Decoded: {8}\n\n'.format( - val0, - server, - val1, - client, - val2, - server, - val3, - val4, - val5) - - outstreams[client].write(message) - - if DEBUG: - print(message[:-2]) - - totalopcodes += 1 - - if val4 and val5: - encodedecode += 1 - elif val4 and not val5: - encodeonly += 1 - elif not val4 and val5: - decodeonly += 1 - elif not val4 and not val5: - notranslation += 1 - - if DEBUG: - print('->EndOfOpcodeLoop: {0}'.format( - val0)) + out_files[server] = open(file_name, 'w') message = \ - 'Statistics -------------------------------------------\n' \ - '|| Server Opcode Type: {0}\n' \ - '|| Handled Opcodes: {1}\n' \ - '|| Bi-directional: {2}\n' \ - '|| Encodes Only: {3}\n' \ - '|| Decodes Only: {4}\n' \ - '|| No Translations: {5}\n' \ - '\n' \ - 'Notes ------------------------------------------------\n' \ - '|| Encodes are Server-to-Client and Decodes are' \ - ' Server-from-Client in context\n' \ - '|| \'Bi-directional\' indicates translations are performed on' \ - ' tx/rx packets\n' \ - '|| \'Encodes Only\' indicates translations only on tx packets' \ - ' - does not exclude direct packet rx\n' \ - '|| \'Decodes Only\' indicates translations only on rx packets' \ - ' - does not exclude direct packet tx\n' \ - '|| \'No Translations\' indicates no translations of tx/rx' \ - ' packets\n'.format( + '>> \'Opcode-Handler\' analysis for \'{0}\' server\n' \ + '>> file generated @ {1}\n' \ + '\n'.format( server, - totalopcodes, - encodedecode, - encodeonly, - decodeonly, - notranslation) + ctime(time())) - outstreams[client].write(message) + out_files[server].write(message) - if DEBUG: - print(message[:-1]) - print('->EndOfClientLoop: {0}'.format( - client)) - - if DEBUG: - print('->EndOfServerLoop: {0}'.format( - server)) + if DEBUG >= 2: + print(message[:-2]) + except: # StandardError as se: + #print(se.message) + + #if DEBUG >= 2: + # print(pickle.dumps(se.args)) + + for client in client_list: + if client in out_files: + out_files[client].close() + del out_files[client] + + if DEBUG >= 2: + print('->OpeningClientStream(exception): {0}'.format(client)) + + for server in server_list: + if server in out_files: + out_files[server].close() + del out_files[server] + + if DEBUG >= 2: + print('->OpeningServerStream(exception): {0}'.format(server)) + + #if 'Report' in out_files: + # out_files['Report'].close() + # del out_files['Report'] + + # if DEBUG >= 2: + # print('->OpeningReportStream(exception)') + + return False return True -def destroyoutputstreams(): - for client in clients: - if client in outstreams.keys(): - outstreams[client].close() - del outstreams[client] +def parseclientopcodedata(): + # TODO: add metrics + for client in client_list: + server_max_len = 0 + + for server in server_list: + if len(server) > server_max_len: + server_max_len = len(server) + + client_keys = client_opcodes[client].keys() + client_keys.sort() + + for client_opcode in client_keys: + handled = client_opcode in server_opcodes + + if handled is True: + encoded = client_opcode in client_encodes[client] + decoded = client_opcode in client_decodes[client] + else: + encoded = 'n/a' + decoded = 'n/a' + + message = 'Opcode: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( + client_opcode, + client_opcodes[client][client_opcode], + handled, + encoded, + decoded) + + for server in server_list: + if client_opcode in server_handlers[server]: + val1 = '{0}'.format(server_opcodes[client_opcode]).zfill(4) + val2 = server_handlers[server][client_opcode] + else: + val1 = '0000' + val2 = 'n/a' + + message += ' Server: {0} ({1}) | Handler: \'{2}\'\n'.format( + server.ljust(len(server) + (server_max_len - len(server)), ' '), + val1, + val2) + + if DEBUG >= 2: + print('->EndOfServerLoop: {0}'.format(server)) + + message += '\n' + + out_files[client].write(message) + + if DEBUG >= 2: + print(message[:-2]) + print('->EndOfOpcodeLoop: {0}'.format(client_opcode)) + + if DEBUG >= 2: + print('->EndOfClientLoop: {0}'.format(client)) + + return True + + +def parseserveropcodedata(): + # TODO: add metrics + for server in server_list: + client_max_len = 0 + + for client in client_list: + if len(client) > client_max_len: + client_max_len = len(client) + + handler_keys = server_handlers[server].keys() + handler_keys.sort() + + for handler_opcode in handler_keys: + message = 'Opcode: {0} ({1}) | Handler: \'{2}\'\n'.format( + handler_opcode, + server_opcodes[handler_opcode], + server_handlers[server][handler_opcode]) + + for client in client_list: + if handler_opcode in client_opcodes[client]: + val1 = client_opcodes[client][handler_opcode] + val2 = 'True' + val3 = '{0}'.format(handler_opcode in client_encodes[client]) + val4 = '{0}'.format(handler_opcode in client_decodes[client]) + else: + val1 = '0x0000' + val2 = 'False' + val3 = 'n/a' + val4 = 'n/a' + + message += ' Client: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( + client.ljust(len(client) + (client_max_len - len(client)), ' '), + val1, + val2.ljust(len(val2) + (len('False') - len(val2)), ' '), + val3.ljust(len(val3) + (len('False') - len(val3)), ' '), + val4.ljust(len(val4) + (len('False') - len(val4)), ' ')) + + if DEBUG >= 2: + print('->EndOfClientLoop: {0}'.format(client)) + + message += '\n' + + out_files[server].write(message) + + if DEBUG >= 2: + print(message[:-2]) + print('->EndOfOpcodeLoop: {0}'.format(handler_opcode)) + + if DEBUG >= 2: + print('->EndOfServerLoop: {0}'.format(server)) + + return True + + +def closeoutputfiles(): + for client in client_list: + if client in out_files: + out_files[client].close() + del out_files[client] + + if DEBUG >= 2: + print('->ClosingClientStream: {0}'.format(client)) + + for server in server_list: + if server in out_files: + out_files[server].close() + del out_files[server] + + if DEBUG >= 2: + print('->ClosingServerStream: {0}'.format(server)) + + #if 'Report' in out_files: + # out_files['Report'].close() + # del out_files['Report'] + + # if DEBUG >= 2: + # print('->ClosingReportStream') - if DEBUG: - print('->DestroyingClientStream: {0}'.format( - client)) - return True From 2aec190afc0dd714f5120727d4f82de1ca6baa8f Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Aug 2014 13:42:14 -0400 Subject: [PATCH 13/22] Support for spells_new field 'uninterruptable' --- common/shareddb.cpp | 2 +- zone/spell_effects.cpp | 2 +- zone/spells.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a64fef404..7c7a99efa 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); - sp[tempid].uninterruptable=atoi(row[146]); + sp[tempid].uninterruptable=atoi(row[146]) != 0; sp[tempid].ResistDiff=atoi(row[147]); sp[tempid].dot_stacking_exempt=atoi(row[148]); sp[tempid].RecourseLink = atoi(row[150]); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 9f6a3866b..8a45cf391 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2711,7 +2711,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (buffslot >= 0) break; - if(IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + if(!spells[spell_id].uninterruptable && IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) InterruptSpell(); break; diff --git a/zone/spells.cpp b/zone/spells.cpp index ca6d80f6f..e9d3eb55a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1034,7 +1034,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, mlog(SPELLS__CASTING, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(SkillChanneling)); - if(MakeRandomFloat(0, 100) > channelchance) { + if(!spells[spell_id].uninterruptable && MakeRandomFloat(0, 100) > channelchance) { mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: interrupted.", spell_id); InterruptSpell(); return; @@ -4684,7 +4684,7 @@ void Mob::Stun(int duration) if(stunned && stunned_timer.GetRemainingTime() > uint32(duration)) return; - if(casting_spell_id) { + if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; if(IsClient()) persistent_casting += aabonuses.PersistantCasting; From b7be8fb6257500dd3f5c5ee42ae1a71bca33d2e2 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Fri, 8 Aug 2014 15:53:53 -0400 Subject: [PATCH 14/22] fix --- zone/spells.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index e9d3eb55a..b7b5dd169 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4685,11 +4685,9 @@ void Mob::Stun(int duration) return; if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { - int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; - if(IsClient()) - persistent_casting += aabonuses.PersistantCasting; + int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting + aabonuses.PersistantCasting; - if(MakeRandomInt(1,99) > persistent_casting) + if(MakeRandomInt(0,99) > persistent_casting) InterruptSpell(); } From e1996e62b0a5a14dfc45522b44dc97ed32d23aee Mon Sep 17 00:00:00 2001 From: Uleat Date: Sat, 9 Aug 2014 20:59:08 -0400 Subject: [PATCH 15/22] Added 'discovery' code for server handlers - still needs some debug/messaging work --- utils/scripts/opcode_handlers.py | 629 ++++++++++++++++++++++--------- 1 file changed, 445 insertions(+), 184 deletions(-) diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index ca4a00d3d..2a1921d5d 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -9,8 +9,8 @@ # include other servers and clients, and other criteria and features. +import sys import os -# import pickle from time import time, ctime @@ -19,8 +19,8 @@ DEBUG = 1 # {0 - normal, 1 - verbose, 2 - in-depth} base_path = os.getcwd()[:-14] # '/utils/scripts' -client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF'] -server_list = ['Login', 'World', 'Zone', 'UCS'] +client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF', 'RoF2', 'ClientTest'] +server_list = ['Login', 'World', 'Zone', 'UCS', 'ServerTest'] client_opcodes = {} server_opcodes = {} @@ -34,49 +34,138 @@ out_files = {} def main(): - print('Loading source data...') + """ Call each method independently and track success """ - if not loadclientopcodes(): - print('Could not load client opcodes...') - return + fault = False + faults = [] - if not loadserveropcodes(): - print('Could not load server opcodes...') - return + print('') - if not loadclienttranslators(): - print('Could not load client translators...') - return - - if not loadserverhandlers(): - print('Could not load server handlers...') - return + if fault is False: + fault = not createoutputdirectory() - print('Creating output streams...') + if fault is True: + faults.append('createoutputdirectory()') - if not createoutputdirectory(): - print('Could not create output directory...') - return - - if not openoutputfiles(): - print('Could not open output files...') - return + if fault is False: + fault = not opendebugfile() - print('Parsing opcode data...') + if fault is True: + faults.append('opendebugfile()') - if not parseclientopcodedata(): - print('Could not parse client opcode data...') - return + if fault is False: + print('Loading source data...') - if not parseserveropcodedata(): - print('Could not parse server opcode data...') - return + if fault is False: + fault = not loadclientopcodes() - print('Destroying output streams...') + if fault is True: + faults.append('loadclientopcodes()') + if fault is False: + fault = not loadserveropcodes() + + if fault is True: + faults.append('loadserveropcodes()') + + if fault is False: + fault = not loadclienttranslators() + + if fault is True: + faults.append('loadclienttranslators()') + + if fault is False: + fault = not loadserverhandlers() + + if fault is True: + faults.append('loadserverhandlers()') + + if fault is False: + fault = not discoverserverhandlers() + + if fault is True: + faults.append('discoverserverhandlers()') + + if fault is False: + fault = not clearemptyserverentries() + + if fault is True: + faults.append('clearemptyserverentries()') + + if fault is False: + print('Creating output streams...') + + if fault is False: + fault = not openoutputfiles() + + if fault is True: + faults.append('openoutputfiles()') + + if fault is False: + print('Parsing opcode data...') + + if fault is False: + fault = not parseclientopcodedata() + + if fault is True: + faults.append('parseclientopcodedata()') + + if fault is False: + fault = not parseserveropcodedata() + + if fault is True: + faults.append('parseserveropcodedata()') + + if fault is False: + print('Destroying output streams...') + + # these should always be processed..verbose or silent if not closeoutputfiles(): - print('Could not close output files...') - return + faults.append('closeoutputfiles()') + + if not closedebugfile(): + faults.append('closedebugfile()') + + if len(faults) > 0: + message = 'Script failed due to errors in:\n' + + for entry in faults: + message += ' {0}'.format(entry) + + print(message) + + return + + +def createoutputdirectory(): + """ Check for output directory - create if does not exist """ + + try: + output_path = '{0}/utils/scripts/opcode_handlers_output'.format(base_path) + + if DEBUG >= 1: + print(output_path) + + if not os.path.exists(output_path): + os.mkdir(output_path) + + return True + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->createoutputdirectory({0})'.format(sys.exc_info()[0])) + + return False + + +def opendebugfile(): + file_name = '{0}/utils/scripts/opcode_handlers_output/DEBUG.txt'.format(base_path) + + if DEBUG >= 1: + print(file_name) + + out_files['DEBUG'] = open(file_name, 'w') + + return True def loadclientopcodes(): @@ -100,12 +189,7 @@ def loadclientopcodes(): with open(file_name, 'r') as data_file: client_opcodes[client] = {} # force empty dictionary to avoid collisions - while True: - data_line = data_file.readline() - - if not data_line: - break - + for data_line in data_file: key_begin = data_line.find('OP_') key_end = data_line.find('=', key_begin) @@ -133,26 +217,23 @@ def loadclientopcodes(): value)) data_file.close() - except: # StandardError as se: - #print(se.message) - - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->loadclientopcodes({0})'.format(sys.exc_info()[0])) bad_clients.append(client) - if len(bad_clients) > 0: - for bad_client in bad_clients: - if DEBUG >= 1: - print('Deleting \'{0}\' client from search criteria...'.format(bad_client)) + for bad_client in bad_clients: + if DEBUG >= 1: + print('Deleting \'{0}\' client from search criteria...'.format(bad_client)) - client_list.remove(bad_client) + client_list.remove(bad_client) - if DEBUG >= 1: - print('Deleting stale entries for \'{0}\' client...'.format(bad_client)) + if DEBUG >= 1: + print('Deleting stale entries for \'{0}\' client...'.format(bad_client)) - if bad_client in client_opcodes: - del client_opcodes[bad_client] + if bad_client in client_opcodes: + del client_opcodes[bad_client] if not len(client_list) > 0: return False @@ -162,23 +243,23 @@ def loadclientopcodes(): def loadserveropcodes(): try: - value = 1 # Server opcodes are auto-enumerated with a starting reference of '1' + value = 0 - file_name = '{0}{1}{2}'.format( + server_opcodes['OP_Unknown'] = value + value += 1 + + if DEBUG >= 2: + print('N[Server](OP_Unknown) = {0}'.format(server_opcodes['OP_Unknown'])) + + file_name = '{0}{1}'.format( base_path, - '/common', - '/emu_oplist.h') + '/common/emu_oplist.h') if DEBUG >= 1: print(file_name) with open(file_name, 'r') as data_file: - while True: - data_line = data_file.readline() - - if not data_line: - break - + for data_line in data_file: val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -197,21 +278,15 @@ def loadserveropcodes(): data_file.close() - file_name = '{0}{1}{2}'.format( + file_name = '{0}{1}'.format( base_path, - '/common', - '/mail_oplist.h') + '/common/mail_oplist.h') if DEBUG >= 1: print(file_name) with open(file_name, 'r') as data_file: - while True: - data_line = data_file.readline() - - if not data_line: - break - + for data_line in data_file: val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -229,11 +304,9 @@ def loadserveropcodes(): server_opcodes[data_line[val_begin:val_end]])) data_file.close() - except: # StandardError as se: - #print(se.message) - - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->loadserveropcodes({0})'.format(sys.exc_info()[0])) return False @@ -263,12 +336,7 @@ def loadclienttranslators(): client_encodes[client] = [] client_decodes[client] = [] - while True: - data_line = data_file.readline() - - if not data_line: - break - + for data_line in data_file: val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -293,11 +361,9 @@ def loadclienttranslators(): data_line[val_begin:val_end] in client_decodes[client])) data_file.close() - except: # StandardError as se: - #print(se.message) - - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->loadclienttranslators({0})'.format(sys.exc_info()[0])) return False @@ -309,40 +375,46 @@ def loadclienttranslators(): def loadserverhandlers(): + """ Load pre-designated SERVER opcode handlers """ + # TODO: handle remarked out definitions in file (i.e., // and /**/); - # TODO: out-going-only handlers need to be added..more research... bad_servers = [] for server in server_list: try: if server == 'Login': - raise + if DEBUG >= 1: + print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + + continue elif server == 'World': - raise + if DEBUG >= 1: + print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + + continue elif server == 'Zone': - file_name = '{0}{1}{2}'.format( + file_name = '{0}{1}'.format( base_path, - '/zone', - '/client_packet.cpp') + '/zone/client_packet.cpp') if DEBUG >= 1: print(file_name) with open(file_name, 'r') as data_file: server_handlers[server] = {} + can_run = False + line_no = 0 - data_line = data_file.readline() - - while not data_line[:19] == 'void MapOpcodes() {': - data_line = data_file.readline() - - if not data_line: - break - - while True: - data_line = data_file.readline() - - if not data_line or data_line[0:1] == '}': + for data_line in data_file: + line_no += 1 + + if can_run is False: + if data_line[:19] == 'void MapOpcodes() {': + can_run = True + + continue + + if data_line[0:1] == '}': break key_begin = data_line.find('OP_') @@ -357,42 +429,55 @@ def loadserverhandlers(): if val_begin < 0 or val_end < 0: continue - server_handlers[server][data_line[key_begin:key_end]] = data_line[val_begin:val_end] + # TODO: add continue on 'in server_opcodes' failure + + if not data_line[key_begin:key_end] in server_handlers[server]: + server_handlers[server][data_line[key_begin:key_end]] = [] + + server_handlers[server][data_line[key_begin:key_end]].append( + '../zone/client_packet.cpp({0}:{1}) \'{2}\''.format( + line_no, + key_begin, + data_line[val_begin:val_end])) if DEBUG >= 2: - print('[{0}][{1}] = {2}'.format( + print('[{0}][{1}]({2}) [{3}]'.format( server, data_line[key_begin:key_end], - server_handlers[server][data_line[key_begin:key_end]])) + data_line[val_begin:val_end], + data_line[val_begin:val_end] in server_handlers[server][data_line[key_begin:key_end]])) data_file.close() elif server == 'UCS': - raise + if DEBUG >= 1: + print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + + continue else: + if DEBUG >= 1: + print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + if DEBUG >= 2: print('->LoadServerHandlers(Someone added a new server and forgot to code for the data load...)') - return False - except: # StandardError as se: - #print(se.message) - - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + continue + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->loadserverhandlers({0})'.format(sys.exc_info()[0])) bad_servers.append(server) - if len(bad_servers) > 0: - for bad_server in bad_servers: - if DEBUG >= 1: - print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) + for bad_server in bad_servers: + if DEBUG >= 1: + print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) - server_list.remove(bad_server) + server_list.remove(bad_server) - if DEBUG >= 1: - print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + if DEBUG >= 1: + print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) - if bad_server in server_handlers: - del server_handlers[bad_server] + if bad_server in server_handlers: + del server_handlers[bad_server] if not len(server_list) > 0: return False @@ -400,39 +485,199 @@ def loadserverhandlers(): return True -def createoutputdirectory(): - try: - output_path = '{0}{1}'.format( - base_path, - '/utils/scripts/opcode_handlers_output') - +def discoverserverhandlers(): + """ Load undefined SERVER opcode handlers using 'discovery' method """ + + locations = {} + + for server in server_list: # initialize lists for any remaining servers + locations[server] = [] + + # manually enter search locations + #if 'Server' in locations: + # locations['Server'].append('//.') + + # TODO: if/how to include perl/lua handlers... + + if 'Login' in locations: + locations['Login'].append('/loginserver/Client.cpp') + locations['Login'].append('/loginserver/ServerManager.cpp') + locations['Login'].append('/loginserver/WorldServer.cpp') + + if 'World' in locations: + locations['World'].append('/world/client.cpp') + + # the bulk of opcodes are handled in 'Zone' - if processing occurs on a different + # server, you will need to manually trace 'ServerPacket' to the deferred location + if 'Zone' in locations: + locations['Zone'].append('/zone/AA.cpp') + locations['Zone'].append('/zone/attack.cpp') + locations['Zone'].append('/zone/bot.cpp') + locations['Zone'].append('/zone/client.cpp') + locations['Zone'].append('/zone/client_packet.cpp') + locations['Zone'].append('/zone/client_process.cpp') + locations['Zone'].append('/zone/command.cpp') + locations['Zone'].append('/zone/corpse.cpp') + locations['Zone'].append('/zone/doors.cpp') + locations['Zone'].append('/zone/effects.cpp') + locations['Zone'].append('/zone/entity.cpp') + locations['Zone'].append('/zone/exp.cpp') + locations['Zone'].append('/zone/groups.cpp') + locations['Zone'].append('/zone/guild.cpp') + locations['Zone'].append('/zone/guild_mgr.cpp') + locations['Zone'].append('/zone/horse.cpp') + locations['Zone'].append('/zone/inventory.cpp') + locations['Zone'].append('/zone/loottables.cpp') + locations['Zone'].append('/zone/merc.cpp') + locations['Zone'].append('/zone/mob.cpp') + locations['Zone'].append('/zone/MobAI.cpp') + locations['Zone'].append('/zone/Object.cpp') + locations['Zone'].append('/zone/pathing.cpp') + locations['Zone'].append('/zone/petitions.cpp') + locations['Zone'].append('/zone/questmgr.cpp') + locations['Zone'].append('/zone/raids.cpp') + locations['Zone'].append('/zone/special_attacks.cpp') + locations['Zone'].append('/zone/spells.cpp') + locations['Zone'].append('/zone/spell_effects.cpp') + locations['Zone'].append('/zone/tasks.cpp') + locations['Zone'].append('/zone/titles.cpp') + locations['Zone'].append('/zone/tradeskills.cpp') + locations['Zone'].append('/zone/trading.cpp') + locations['Zone'].append('/zone/trap.cpp') + locations['Zone'].append('/zone/tribute.cpp') + locations['Zone'].append('/zone/worldserver.cpp') + locations['Zone'].append('/zone/zone.cpp') + locations['Zone'].append('/zone/zonedb.cpp') + locations['Zone'].append('/zone/zoning.cpp') + + if 'UCS' in locations: + locations['UCS'].append('/ucs/clientlist.cpp') + locations['UCS'].append('/ucs/database.cpp') + + for server in server_list: + if not server in server_handlers: + server_handlers[server] = {} + + for location in locations[server]: + try: + file_name = '{0}{1}'.format( + base_path, + location) + + if DEBUG >= 1: + print(file_name) + + with open(file_name, 'r') as data_file: + line_no = 0 + hint = 'Near beginning of file' + + for data_line in data_file: + line_no += 1 + + if data_line[:1].isalpha(): + hint_end = data_line.find('(') + + if not hint_end < 0: + hint_begin = hint_end - 1 + + while not hint_begin < 0: + if data_line[(hint_begin - 1):hint_begin].isspace(): + if not data_line[hint_begin:(hint_begin + 1)].isalpha(): + hint_begin += 1 + + hint = 'Near {0}'.format(data_line[hint_begin:hint_end]) + + break + + hint_begin -= 1 + + op_begin = data_line.find('OP_') + + if op_begin < 0: + continue + + if data_line[(op_begin - 20):op_begin] == 'EQApplicationPacket(': + key_begin = op_begin + key_end = data_line.find(',', key_begin) + elif data_line[(op_begin - 12):op_begin] == '->SetOpcode(': + key_begin = op_begin + key_end = data_line.find(')', key_begin) + elif data_line[(op_begin - 5):op_begin] == 'case ': + key_begin = op_begin + key_end = data_line.find(':', key_begin) + else: + continue + + if key_end < 0: + continue + + if not data_line[key_begin:key_end] in server_opcodes: + out_files['DEBUG'].write('Illegal Opcode Found: ..{0} ({1}:{2}) \'{3}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end] + )) + + continue + + if not data_line[key_begin:key_end] in server_handlers[server]: + server_handlers[server][data_line[key_begin:key_end]] = [] + + if not data_line in server_handlers[server][data_line[key_begin:key_end]]: + server_handlers[server][data_line[key_begin:key_end]].append( + '..{0}({1}:{2}) \'{3}\''.format( + location, + line_no, + key_begin, + hint)) + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->discoverserverhandlers({0})'.format(sys.exc_info()[0])) + + return True + + +def clearemptyserverentries(): + bad_servers = [] + + for server in server_list: + if len(server_handlers[server]) == 0: + bad_servers.append(server) + else: + bad_opcodes = [] + + for opcode in server_handlers[server]: + if len(server_handlers[server][opcode]) == 0: + bad_opcodes.append(opcodes) + + for bad_opcode in bad_opcodes: + del server_handlers[server][bad_opcode] + + if len(server_handlers[server]) == 0: + bad_servers.append(server) + + for bad_server in bad_servers: if DEBUG >= 1: - print(output_path) - - if not os.path.exists(output_path): - os.mkdir(output_path) - - return True - except: # StandardError as se: - #print(se.message) + print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) + print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + del server_handlers[bad_server] + server_list.remove(bad_server) - return False + return True def openoutputfiles(): + """ Open output files in 'w' mode - create/overwrite mode """ + try: - #file_name = '{0}{1}{2}'.format( - # base_path, - # '/utils/scripts/opcode_handlers_output/', - # 'Report.txt') + file_name = '{0}/utils/scripts/opcode_handlers_output/Report.txt'.format(base_path) - #if DEBUG >= 1: - # print(file_name) + if DEBUG >= 1: + print(file_name) - #out_files['Report'] = open(file_name, 'w') + out_files['Report'] = open(file_name, 'w') for client in client_list: file_name = '{0}{1}{2}{3}'.format( @@ -481,11 +726,9 @@ def openoutputfiles(): if DEBUG >= 2: print(message[:-2]) - except: # StandardError as se: - #print(se.message) - - #if DEBUG >= 2: - # print(pickle.dumps(se.args)) + except: + if DEBUG >= 2: + print('EXCEPTION ERROR->openoutputfiles({0})'.format(sys.exc_info()[0])) for client in client_list: if client in out_files: @@ -503,12 +746,12 @@ def openoutputfiles(): if DEBUG >= 2: print('->OpeningServerStream(exception): {0}'.format(server)) - #if 'Report' in out_files: - # out_files['Report'].close() - # del out_files['Report'] + if 'Report' in out_files: + out_files['Report'].close() + del out_files['Report'] - # if DEBUG >= 2: - # print('->OpeningReportStream(exception)') + if DEBUG >= 2: + print('->OpeningReportStream(exception)') return False @@ -534,8 +777,8 @@ def parseclientopcodedata(): encoded = client_opcode in client_encodes[client] decoded = client_opcode in client_decodes[client] else: - encoded = 'n/a' - decoded = 'n/a' + encoded = 'N/A' + decoded = 'N/A' message = 'Opcode: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( client_opcode, @@ -545,17 +788,18 @@ def parseclientopcodedata(): decoded) for server in server_list: - if client_opcode in server_handlers[server]: - val1 = '{0}'.format(server_opcodes[client_opcode]).zfill(4) - val2 = server_handlers[server][client_opcode] - else: - val1 = '0000' - val2 = 'n/a' + if client_opcode in server_handlers[server] and len(server_handlers[server][client_opcode]) > 0: + handler_list = server_handlers[server][client_opcode] + handler_list.sort() - message += ' Server: {0} ({1}) | Handler: \'{2}\'\n'.format( - server.ljust(len(server) + (server_max_len - len(server)), ' '), - val1, - val2) + for handler_entry in handler_list: + message += ' Server: {0} ({1}) | Handler: {2}\n'.format( + server.ljust(len(server) + (server_max_len - len(server)), ' '), + '{0}'.format(server_opcodes[client_opcode]).zfill(4), + handler_entry) + else: + message += ' Server: {0} (0000) | Handler: N/A\n'.format( + server.ljust(len(server) + (server_max_len - len(server)), ' ')) if DEBUG >= 2: print('->EndOfServerLoop: {0}'.format(server)) @@ -587,10 +831,16 @@ def parseserveropcodedata(): handler_keys.sort() for handler_opcode in handler_keys: - message = 'Opcode: {0} ({1}) | Handler: \'{2}\'\n'.format( - handler_opcode, - server_opcodes[handler_opcode], - server_handlers[server][handler_opcode]) + handler_list = server_handlers[server][handler_opcode] + handler_list.sort() + + message = '' + + for handler_entry in handler_list: + message += 'Opcode: {0} ({1}) | Handler: {2}\n'.format( + handler_opcode, + server_opcodes[handler_opcode], + handler_entry) for client in client_list: if handler_opcode in client_opcodes[client]: @@ -601,8 +851,8 @@ def parseserveropcodedata(): else: val1 = '0x0000' val2 = 'False' - val3 = 'n/a' - val4 = 'n/a' + val3 = 'N/A' + val4 = 'N/A' message += ' Client: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( client.ljust(len(client) + (client_max_len - len(client)), ' '), @@ -645,12 +895,23 @@ def closeoutputfiles(): if DEBUG >= 2: print('->ClosingServerStream: {0}'.format(server)) - #if 'Report' in out_files: - # out_files['Report'].close() - # del out_files['Report'] + if 'Report' in out_files: + out_files['Report'].close() + del out_files['Report'] - # if DEBUG >= 2: - # print('->ClosingReportStream') + if DEBUG >= 2: + print('->ClosingReportStream') + + return True + + +def closedebugfile(): + if 'DEBUG' in out_files: + out_files['DEBUG'].close() + del out_files['DEBUG'] + + if DEBUG >= 2: + print('->ClosingDEBUGStream') return True From d7eb28c7f82dc94b7202b17928f839f2b542f22d Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 10 Aug 2014 02:45:21 -0700 Subject: [PATCH 16/22] Travis CI file --- .travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..5bdd64324 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: cpp +compiler: gcc +before_install: + - sudo apt-get update -qq + - sudo apt-get install -y libmysqlclient-dev libperl-dev libboost-dev liblua5.1-0-dev zlib1g-dev +script: + - cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON + - make + - ./bin/tests +branches: + only: master +notifications: + email: false + irc: + channels: "irc.eqemulator.net#eqemucoders" +os: linux \ No newline at end of file From 478eb35a3bbb58f673a615010a805388f99c3f18 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 10 Aug 2014 08:37:30 -0400 Subject: [PATCH 17/22] Rule ArcheryHitPenalty fix for calc mistake from prior update. --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 2403293e6..3195b4234 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -311,7 +311,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database if(skillinuse == SkillArchery) - hitBonus -= hitBonus*(RuleR(Combat, ArcheryHitPenalty)*100.0f); + hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty); //Calculate final chance to hit chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f); From 99b25f42e4a52e4dbc61b25b3802c6ff10652f9f Mon Sep 17 00:00:00 2001 From: Michael Cook Date: Sun, 10 Aug 2014 12:42:43 -0400 Subject: [PATCH 18/22] Add Travis-CI bling --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index edee03cca..bee563054 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ EQEmu - Custom Game Implementation for EverQuest +[![Build Status](https://api.travis-ci.org/EQEmu/Server.svg)](https://travis-ci.org/EQEmu/Server) + Dependencies can be obtained at http://eqemu.github.io More Information: https://github.com/EQEmu/Server/wiki From c02cb2c343d352fbf74420fee588be42807de940 Mon Sep 17 00:00:00 2001 From: Michael Cook Date: Sun, 10 Aug 2014 13:08:50 -0400 Subject: [PATCH 19/22] Proper bling instead of copy paste for another [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bee563054..d7ca01121 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ EQEmu - Custom Game Implementation for EverQuest -[![Build Status](https://api.travis-ci.org/EQEmu/Server.svg)](https://travis-ci.org/EQEmu/Server) +[![Build Status](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) Dependencies can be obtained at http://eqemu.github.io From 8940e987c1d7fba36a1f939997590d904ee494b7 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 10 Aug 2014 15:56:58 -0400 Subject: [PATCH 20/22] Fix some compiler warnings --- client_files/export/main.cpp | 4 ++-- client_files/import/main.cpp | 4 ++-- common/shareddb.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index 8aa29bea6..d89976b88 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -70,7 +70,7 @@ void ExportSpells(SharedDatabase *db) { } char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM spells_new ORDER BY id"; + const char *query = "SELECT * FROM spells_new ORDER BY id"; MYSQL_RES *result; MYSQL_ROW row; if(db->RunQuery(query, strlen(query), errbuf, &result)) { @@ -176,7 +176,7 @@ void ExportBaseData(SharedDatabase *db) { } char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM base_data ORDER BY level, class"; + const char *query = "SELECT * FROM base_data ORDER BY level, class"; MYSQL_RES *result; MYSQL_ROW row; if(db->RunQuery(query, strlen(query), errbuf, &result)) { diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 8115600bf..d902bc98e 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -61,7 +61,7 @@ int main(int argc, char **argv) { int GetSpellColumns(SharedDatabase *db) { char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "DESCRIBE spells_new"; + const char *query = "DESCRIBE spells_new"; MYSQL_RES *result; MYSQL_ROW row; int res = 0; @@ -234,4 +234,4 @@ void ImportBaseData(SharedDatabase *db) { } fclose(f); -} \ No newline at end of file +} diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 7c7a99efa..7a9858789 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1775,7 +1775,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { int SharedDatabase::GetMaxBaseDataLevel() { char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT MAX(level) FROM base_data"; + const char *query = "SELECT MAX(level) FROM base_data"; MYSQL_RES *result; MYSQL_ROW row; int32 ret = 0; @@ -1826,7 +1826,7 @@ bool SharedDatabase::LoadBaseData() { void SharedDatabase::LoadBaseData(void *data, int max_level) { char *base_ptr = reinterpret_cast(data); char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; + const char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; MYSQL_RES *result; MYSQL_ROW row; From 8b2dba97159cff2d382952dd1c7b3492f84e9b91 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 10 Aug 2014 20:13:01 -0400 Subject: [PATCH 21/22] ST_PetMaster target type support added Targets pets master. (Works for regular and swarm pets) --- common/spdat.h | 4 ++-- zone/spells.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index aaf29e61a..40f5e658f 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -131,10 +131,10 @@ typedef enum { /* 41 */ ST_Group = 0x29, /* 42 */ ST_Directional = 0x2a, //ae around this target between two angles /* 43 */ ST_GroupClientAndPet = 0x2b, -/* 44 */ ST_Beam = 0x2c, //like directional but facing in front of you always +/* 44 */ //ST_Beam = 0x2c, //like directional but facing in front of you always /* 45 */ //ST_Ring = 0x2d, // Like a mix of PB ae + rain spell(has ae duration) /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target -/* 47 */ //ST_PetMaster = 0x2e, // uses the master as target +/* 47 */ ST_PetMaster = 0x2f, // uses the master as target } SpellTargetType; typedef enum { diff --git a/zone/spells.cpp b/zone/spells.cpp index b7b5dd169..5d891629e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1780,6 +1780,24 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_PetMaster: + { + + Mob *owner = nullptr; + + if (IsPet()) + owner = GetOwner(); + else if ((IsNPC() && CastToNPC()->GetSwarmOwner())) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + + if (!owner) + return false; + + spell_target = owner; + CastAction = SingleTarget; + break; + } + default: { mlog(SPELLS__CASTING_ERR, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); From 091047d08be6a11caf1018b44da2e3496a6fee51 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 10 Aug 2014 23:07:05 -0400 Subject: [PATCH 22/22] Pretty up the read me [skip ci] --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d7ca01121..f2245dcf4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,53 @@ -EQEmu - Custom Game Implementation for EverQuest +EQEmu +=== [![Build Status](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) -Dependencies can be obtained at http://eqemu.github.io +Overview +--- + +EQEmu is a custom server implementation for EverQuest + +Dependencies +--- + +For Windows: http://eqemu.github.io + +Login Server dependencies for Windows/Linux/OSX: http://eqemu.github.io + +For Debian based distros (adjust to your local flavor): + +- libmysqlclient-dev +- libperl-dev +- liblua5.1-0-dev (5.2 should work as well) +- libboost-dev + +Further instructions on building the source can be found on the +[wiki](http://wiki.eqemulator.org/i?M=Wiki). + +Bug reports +--- + +Please use the [issue tracker](issue-tracker) provided by GitHub to send us bug +reports or feature requests. + +The [EQEmu Forums](http://www.eqemulator.org/forums/) also have forums to submit +bugs/get help with bugs. + +Contributions +--- + +The preferred way to contribute is to fork the repo and submit a pull request on +GitHub. If you need help with your changes, you can always post on the forums or +try IRC. You can also post unified diffs (`git diff` should do the trick) on the +[Server Code Submissions](http://www.eqemulator.org/forums/forumdisplay.php?f=669) +forum, although pull requests will be much quicker and easier on all parties. + +Contact +--- + - **User IRC Channel**: `#eqemu` on `irc.eqemulator.net` + - **Developer IRC Channel**: `#eqemucoders` on `irc.eqemulator.net` + +- [EQEmulator Forums](http://www.eqemulator.org/forums) +- [EQEmulator Wiki](http://wiki.eqemulator.org/i?M=Wiki) -More Information: https://github.com/EQEmu/Server/wiki