diff options
-rw-r--r-- | sql/mangos_spell_check.sql | 48 | ||||
-rw-r--r-- | src/game/Chat.cpp | 3 | ||||
-rw-r--r-- | src/game/Chat.h | 1 | ||||
-rw-r--r-- | src/game/Debugcmds.cpp | 9 | ||||
-rw-r--r-- | src/game/SpellMgr.cpp | 266 | ||||
-rw-r--r-- | src/game/SpellMgr.h | 12 | ||||
-rw-r--r-- | src/shared/Util.h | 12 |
7 files changed, 337 insertions, 14 deletions
diff --git a/sql/mangos_spell_check.sql b/sql/mangos_spell_check.sql new file mode 100644 index 00000000000..5f34ffdc64e --- /dev/null +++ b/sql/mangos_spell_check.sql @@ -0,0 +1,48 @@ +/* This file contain expected by code spell properties from simple existance to spell family mask and icons. */ + +/* This data let easy detect spell properties change at client version */ +/* support switch and update or drop problematic code associated with checked*/ +/* spell data. */ + +/* sql file not required for server work but expected to be applied to */ +/* MaNGOS DB if you plan use .debug spellcheck command in console. */ + +/* To code commiters: */ +/* */ +/* If you commit code that include explicit used spell id or checks base at */ +/* spell family mask (and etc) please include same data in this table always.*/ +/* At least for parts already added in table. */ +/* File not required sql updates way to support, and always expected full */ +/* re-adding before command use. So need just adding new line to file. */ + +/* Current table fill progress state: */ +/* SpellEffect.cpp from start until end of Spell::EffectInstaKill */ + +DROP TABLE IF EXISTS spell_check; +CREATE TABLE `spell_check` ( + spellid mediumint(7) unsigned NOT NULL default '0', + SpellFamilyName smallint(5) NOT NULL default '-1', + SpellFamilyMaskA bigint(30) NOT NULL default '-1', /* 0xFFFFFFFFFFFFFFFF */ + SpellFamilyMaskB int(10) NOT NULL default '-1', /* 0xFFFFFFFF */ + SpellIcon int(10) NOT NULL default '-1', + SpellVisual int(10) NOT NULL default '-1', + SpellCategory int(10) NOT NULL default '-1', + EffectType int(10) NOT NULL default '-1', + EffectAura int(10) NOT NULL default '-1', + EffectIdx tinyint(3) NOT NULL default '-1', + Name varchar(40) NOT NULL default '', + Code varchar(40) NOT NULL default '', + PRIMARY KEY (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,Code) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code) VALUES +/* sorted by spell ids */ +/*id fm familyMaskA fmMaskB icon vis cat eff aur ef name code */ +(18788,-1, -1, -1, -1, -1, -1, 1, -1,-1,'Demonic Sacrifice', 'Spell::EffectInstaKill'), +(18789,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'), +(18790,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'), +(18791,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'), +(18792,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'); +/* sorted by spell names */ +/*id fm familyMaskA fmMaskB icon vis cat eff aur ef name code */ +/* no at this moment */ diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index af643b50ce4..9ae5678c752 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -163,6 +163,7 @@ ChatCommand * ChatHandler::getCommandTable() { "setaurastate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetAuraStateCommand, "", NULL }, { "setitemflag", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemFlagCommand, "", NULL }, { "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValueCommand, "", NULL }, + { "spellcheck", SEC_CONSOLE, true, &ChatHandler::HandleDebugSpellCheckCommand, "", NULL }, { "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL }, { "setvid", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetVehicleId, "", NULL }, { "entervehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugEnterVehicle, "", NULL }, @@ -628,7 +629,7 @@ ChatCommand * ChatHandler::getCommandTable() { "go", SEC_MODERATOR, false, NULL, "", goCommandTable }, { "learn", SEC_MODERATOR, false, NULL, "", learnCommandTable }, { "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable }, - { "debug", SEC_MODERATOR, false, NULL, "", debugCommandTable }, + { "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable }, { "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable }, { "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable}, { "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable }, diff --git a/src/game/Chat.h b/src/game/Chat.h index a2bc23217ac..55116bae711 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -148,6 +148,7 @@ class ChatHandler bool HandleDebugEnterVehicle(const char * args); bool HandleDebugSetValueCommand(const char* args); bool HandleDebugSpawnVehicle(const char * args); + bool HandleDebugSpellCheckCommand(const char* args); bool HandleDebugUpdateCommand(const char* args); bool HandleDebugUpdateWorldStateCommand(const char* args); diff --git a/src/game/Debugcmds.cpp b/src/game/Debugcmds.cpp index eabf720f17b..e504865cbc3 100644 --- a/src/game/Debugcmds.cpp +++ b/src/game/Debugcmds.cpp @@ -36,6 +36,7 @@ #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" +#include "SpellMgr.h" bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args) { @@ -800,6 +801,14 @@ bool ChatHandler::HandleDebugSpawnVehicle(const char* args) return true; } +bool ChatHandler::HandleDebugSpellCheckCommand(const char* /*args*/) +{ + sLog.outString( "Check expected in code spell properties base at table 'spell_check' content..."); + spellmgr.CheckUsedSpells("spell_check"); + return true; +} + + bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/) { const char* stuffingString = "This is a dummy string to push the packet's size beyond 128000 bytes. "; diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 9e0b69d0777..dfa25cef538 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -889,17 +889,6 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI return false; } -bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId) -{ - SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) return false; - - for (int i = 0; i < 3; ++i) - if (spellproto->EffectApplyAuraName[i] == auraType) - return true; - return false; -} - SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) { // talents that learn spells can have stance requirements that need ignore @@ -2593,6 +2582,261 @@ void SpellMgr::LoadSkillLineAbilityMap() sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); } +void SpellMgr::CheckUsedSpells(char const* table) +{ + uint32 countSpells = 0; + uint32 countMasks = 0; + + // 0 1 2 3 4 5 6 7 8 9 10 11 + QueryResult *result = WorldDatabase.PQuery("SELECT spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code FROM %s",table); + + if( !result ) + { + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb("`%s` table is empty!",table); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint32 spell = fields[0].GetUInt32(); + int32 family = fields[1].GetInt32(); + uint64 familyMaskA = fields[2].GetUInt64(); + uint32 familyMaskB = fields[3].GetUInt32(); + flag96 familyMask(familyMaskA, familyMaskB); + int32 spellIcon = fields[4].GetInt32(); + int32 spellVisual = fields[5].GetInt32(); + int32 category = fields[6].GetInt32(); + int32 effectType = fields[7].GetInt32(); + int32 auraType = fields[8].GetInt32(); + int32 effectIdx = fields[9].GetInt32(); + std::string name = fields[10].GetCppString(); + std::string code = fields[11].GetCppString(); + + // checks of correctness requirements itself + + if (family < -1 || family > SPELLFAMILY_PET) + { + sLog.outError("Table '%s' for spell %u have wrong SpellFamily value(%u), skipped.",table,spell,family); + continue; + } + + // TODO: spellIcon check need dbc loading + if (spellIcon < -1) + { + sLog.outError("Table '%s' for spell %u have wrong SpellIcon value(%u), skipped.",table,spell,spellIcon); + continue; + } + + // TODO: spellVisual check need dbc loading + if (spellVisual < -1) + { + sLog.outError("Table '%s' for spell %u have wrong SpellVisual value(%u), skipped.",table,spell,spellVisual); + continue; + } + + // TODO: for spellCategory better check need dbc loading + if (category < -1 || category >=0 && sSpellCategoryStore.find(category) == sSpellCategoryStore.end()) + { + sLog.outError("Table '%s' for spell %u have wrong SpellCategory value(%u), skipped.",table,spell,category); + continue; + } + + if (effectType < -1 || effectType >= TOTAL_SPELL_EFFECTS) + { + sLog.outError("Table '%s' for spell %u have wrong SpellEffect type value(%u), skipped.",table,spell,effectType); + continue; + } + + if (auraType < -1 || auraType >= TOTAL_AURAS) + { + sLog.outError("Table '%s' for spell %u have wrong SpellAura type value(%u), skipped.",table,spell,auraType); + continue; + } + + if (effectIdx < -1 || effectIdx >= 3) + { + sLog.outError("Table '%s' for spell %u have wrong EffectIdx value(%u), skipped.",table,spell,effectIdx); + continue; + } + + // now checks of requirements + + if(spell) + { + ++countSpells; + + SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell); + if(!spellEntry) + { + sLog.outError("Spell %u '%s' not exist but used in %s.",spell,name.c_str(),code.c_str()); + continue; + } + + if(family >= 0 && spellEntry->SpellFamilyName != family) + { + sLog.outError("Spell %u '%s' family(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellFamilyName,family,code.c_str()); + continue; + } + + if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF) + { + if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000) + { + if(spellEntry->SpellFamilyFlags) + { + sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",spell,name.c_str(),familyMaskA,familyMaskB,code.c_str()); + continue; + } + + } + else + { + if(!(spellEntry->SpellFamilyFlags & familyMask)) + { + sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",spell,name.c_str(),familyMaskA,familyMaskB,code.c_str()); + continue; + } + + } + } + + if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon) + { + sLog.outError("Spell %u '%s' icon(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellIconID,spellIcon,code.c_str()); + continue; + } + + if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual) + { + sLog.outError("Spell %u '%s' visual(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellVisual[0],spellVisual,code.c_str()); + continue; + } + + if(category >= 0 && spellEntry->Category != category) + { + sLog.outError("Spell %u '%s' category(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->Category,category,code.c_str()); + continue; + } + + if(effectIdx >= 0) + { + if(effectType >= 0 && spellEntry->Effect[effectIdx] != effectType) + { + sLog.outError("Spell %u '%s' effect%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,effectType,code.c_str()); + continue; + } + + if(auraType >= 0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType) + { + sLog.outError("Spell %u '%s' aura%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,auraType,code.c_str()); + continue; + } + + } + else + { + if(effectType >= 0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType))) + { + sLog.outError("Spell %u '%s' not have effect %u but used in %s.",spell,name.c_str(),effectType,code.c_str()); + continue; + } + + if(auraType >= 0 && !IsSpellHaveAura(spellEntry,AuraType(auraType))) + { + sLog.outError("Spell %u '%s' not have aura %u but used in %s.",spell,name.c_str(),auraType,code.c_str()); + continue; + } + } + } + else + { + ++countMasks; + + bool found = false; + for(uint32 spellId = 1; spellId < sSpellStore.GetNumRows(); ++spellId) + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId); + if(!spellEntry) + continue; + + if(family >=0 && spellEntry->SpellFamilyName != family) + continue; + + if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF) + { + if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000) + { + if(spellEntry->SpellFamilyFlags) + continue; + } + else + { + if(!(spellEntry->SpellFamilyFlags & familyMask)) + continue; + } + } + + if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon) + continue; + + if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual) + continue; + + if(category >= 0 && spellEntry->Category != category) + continue; + + if(effectIdx >= 0) + { + if(effectType >=0 && spellEntry->Effect[effectIdx] != effectType) + continue; + + if(auraType >=0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType) + continue; + } + else + { + if(effectType >=0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType))) + continue; + + if(auraType >=0 && !IsSpellHaveAura(spellEntry,AuraType(auraType))) + continue; + } + + found = true; + break; + } + + if(!found) + { + if(effectIdx >= 0) + sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect%d(%i) aura%d(%i) but used in %s", + name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectIdx+1,effectType,effectIdx+1,auraType,code.c_str()); + else + sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect(%i) aura(%i) but used in %s", + name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectType,auraType,code.c_str()); + continue; + } + } + + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Checked %u spells and %u spell masks", countSpells, countMasks ); +} + DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) { // Explicit Diminishing Groups diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index e087ec66502..2dde39abba9 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -193,6 +193,14 @@ inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect) return false; } +inline bool IsSpellHaveAura(SpellEntry const *spellInfo, AuraType aura) +{ + for(int i= 0; i < 3; ++i) + if(SpellEffects(spellInfo->EffectApplyAuraName[i])==aura) + return true; + return false; +} + //bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); inline bool IsSealSpell(SpellEntry const *spellInfo) @@ -256,8 +264,6 @@ bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool de bool IsSingleTargetSpell(SpellEntry const *spellInfo); bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2); -bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId); - extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) @@ -1045,6 +1051,8 @@ class SpellMgr public: static SpellMgr& Instance(); + void CheckUsedSpells(char const* table); + // Loading data at server startup void LoadSpellChains(); void LoadSpellRequired(); diff --git a/src/shared/Util.h b/src/shared/Util.h index ddbf968b2c2..496411d8be3 100644 --- a/src/shared/Util.h +++ b/src/shared/Util.h @@ -321,6 +321,11 @@ uint32 CreatePIDFile(const std::string& filename); #ifndef _FLAG96 #define _FLAG96 +#ifndef PAIR64_HIPART +#define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & UI64LIT(0x00000000FFFFFFFF)) +#define PAIR64_LOPART(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF)) +#endif + class flag96 { private: @@ -333,6 +338,13 @@ public: part[2]=p3; } + flag96(uint64 p1, uint32 p2) + { + part[0]=PAIR64_LOPART(p1); + part[1]=PAIR64_HIPART(p1); + part[2]=p2; + } + inline bool IsEqual(uint32 p1=0, uint32 p2=0, uint32 p3=0) const { return ( |