diff options
Diffstat (limited to 'src/shared/Database')
34 files changed, 5230 insertions, 0 deletions
diff --git a/src/shared/Database/DBCStores.cpp b/src/shared/Database/DBCStores.cpp new file mode 100644 index 00000000000..623b9652fb3 --- /dev/null +++ b/src/shared/Database/DBCStores.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DBCStores.h" +//#include "DataStore.h" +#include "Policies/SingletonImp.h" +#include "Log.h" +#include "ProgressBar.h" + +#include "DBCfmt.cpp" + +#include <map> + +typedef std::map<uint16,uint32> AreaFlagByAreaID; +typedef std::map<uint32,uint32> AreaFlagByMapID; + +DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); +static AreaFlagByAreaID sAreaFlagByAreaID; +static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files + +DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt); +DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt); +DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt); +DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); +DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); +DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); +DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt); +DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt); +DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt); +DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt); + +DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore(DurabilityQualityfmt); +DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt); + +DBCStorage <EmotesTextEntry> sEmotesTextStore(EmoteEntryfmt); + +typedef std::map<uint32,SimpleFactionsList> FactionTeamMap; +static FactionTeamMap sFactionTeamMap; +DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt); +DBCStorage <FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt); + +DBCStorage <GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt); + +DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore(GtCombatRatingsfmt); +DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt); +DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt); +DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt); +DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore(GtChanceToSpellCritfmt); +DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore(GtOCTRegenHPfmt); +//DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently +DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore(GtRegenHPPerSptfmt); +DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptfmt); +DBCStorage <ItemEntry> sItemStore(Itemfmt); +//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); +//DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently +DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore(ItemExtendedCostEntryfmt); +DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore(ItemRandomPropertiesfmt); +DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt); +DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt); + +DBCStorage <LockEntry> sLockStore(LockEntryfmt); + +DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt); +DBCStorage <MapEntry> sMapStore(MapEntryfmt); + +DBCStorage <QuestSortEntry> sQuestSortStore(QuestSortEntryfmt); + +DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); + +DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt); +DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt); + +DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt); + +DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); +DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); +DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt); +SpellCategoryStore sSpellCategoryStore; +PetFamilySpellsStore sPetFamilySpellsStore; + +DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt); +DBCStorage <SpellDurationEntry> sSpellDurationStore(SpellDurationfmt); +DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt); +DBCStorage <SpellRadiusEntry> sSpellRadiusStore(SpellRadiusfmt); +DBCStorage <SpellRangeEntry> sSpellRangeStore(SpellRangefmt); +DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt); +DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt); +DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt); +TalentSpellPosMap sTalentSpellPosMap; +DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt); + +// store absolute bit position for first rank for talent inspect +typedef std::map<uint32,uint32> TalentInspectMap; +static TalentInspectMap sTalentPosInInspect; +static TalentInspectMap sTalentTabSizeInInspect; +static uint32 sTalentTabPages[12/*MAX_CLASSES*/][3]; + +DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt); +TaxiMask sTaxiNodesMask; + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathSetBySource sTaxiPathSetBySource; +DBCStorage <TaxiPathEntry> sTaxiPathStore(TaxiPathEntryfmt); + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathNodesByPath sTaxiPathNodesByPath; +struct TaxiPathNodeEntry +{ + uint32 path; + uint32 index; + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt); + +DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt); + +DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt); +DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt); + +typedef std::list<std::string> StoreProblemList; + +static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, std::string filename) +{ + sLog.outError("ERROR: Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).",filename.c_str(),fsize,rsize); + + // assert must fail after function call + return false; +} + +template<class T> +inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage<T>& storage, std::string dbc_path, std::string filename) +{ + // compatibility format and C++ structure sizes + assert(DBCFile::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFile::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename)); + + std::string dbc_filename = dbc_path + filename; + if(storage.Load(dbc_filename.c_str())) + { + bar.step(); + for(uint8 i = 0; i < MAX_LOCALE; ++i) + { + if(!(availableDbcLocales & (1 << i))) + continue; + + std::string dbc_filename_loc = dbc_path + localeNames[i] + "/" + filename; + if(!storage.LoadStringsFrom(dbc_filename_loc.c_str())) + availableDbcLocales &= ~(1<<i); // mark as not available for speedup next checks + } + } + else + { + // sort problematic dbc to (1) non compatible and (2) non-existed + FILE * f=fopen(dbc_filename.c_str(),"rb"); + if(f) + { + char buf[100]; + snprintf(buf,100," (exist, but have %d fields instead %d) Wrong client version DBC file?",storage.GetFieldCount(),strlen(storage.GetFormat())); + errlist.push_back(dbc_filename + buf); + fclose(f); + } + else + errlist.push_back(dbc_filename); + } +} + +void LoadDBCStores(std::string dataPath) +{ + std::string dbcPath = dataPath+"dbc/"; + + const uint32 DBCFilesCount = 56; + + barGoLink bar( DBCFilesCount ); + + StoreProblemList bad_dbc_files; + uint32 availableDbcLocales = 0xFFFFFFFF; + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaStore, dbcPath,"AreaTable.dbc"); + + // must be after sAreaStore loading + for(uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 + { + if(AreaTableEntry const* area = sAreaStore.LookupEntry(i)) + { + // fill AreaId->DBC records + sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID),area->exploreFlag)); + + // fill MapId->DBC records ( skip sub zones and continents ) + if(area->zone==0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 ) + sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag)); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBankBagSlotPricesStore, dbcPath,"BankBagSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharTitlesStore, dbcPath,"CharTitles.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChatChannelsStore, dbcPath,"ChatChannels.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrClassesStore, dbcPath,"ChrClasses.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); + for (uint32 i=0;i<sFactionStore.GetNumRows(); ++i) + { + FactionEntry const * faction = sFactionStore.LookupEntry(i); + if (faction && faction->team) + { + SimpleFactionsList &flist = sFactionTeamMap[faction->team]; + flist.push_back(i); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtCombatRatingsStore, dbcPath,"gtCombatRatings.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritBaseStore, dbcPath,"gtChanceToMeleeCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritStore, dbcPath,"gtChanceToMeleeCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritBaseStore, dbcPath,"gtChanceToSpellCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritStore, dbcPath,"gtChanceToSpellCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenHPStore, dbcPath,"gtOCTRegenHP.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenMPStore, dbcPath,"gtOCTRegenMP.dbc"); -- not used currently + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenHPPerSptStore, dbcPath,"gtRegenHPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenMPPerSptStore, dbcPath,"gtRegenMPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemStore, dbcPath,"Item.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemDisplayInfoStore, dbcPath,"ItemDisplayInfo.dbc"); -- not used currently + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemCondExtCostsStore, dbcPath,"ItemCondExtCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemExtendedCostStore, dbcPath,"ItemExtendedCost.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomPropertiesStore,dbcPath,"ItemRandomProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomSuffixStore, dbcPath,"ItemRandomSuffix.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemSetStore, dbcPath,"ItemSet.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLockStore, dbcPath,"Lock.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMailTemplateStore, dbcPath,"MailTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapStore, dbcPath,"Map.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestSortStore, dbcPath,"QuestSort.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sRandomPropertiesPointsStore, dbcPath,"RandPropPoints.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineStore, dbcPath,"SkillLine.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineAbilityStore, dbcPath,"SkillLineAbility.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSoundEntriesStore, dbcPath,"SoundEntries.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc"); + for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const * spell = sSpellStore.LookupEntry(i); + if(spell && spell->Category) + sSpellCategoryStore[spell->Category].insert(i); + + // DBC not support uint64 fields but SpellEntry have SpellFamilyFlags mapped at 2 uint32 fields + // uint32 field already converted to bigendian if need, but must be swapped for correct uint64 bigendian view + #if MANGOS_ENDIAN == MANGOS_BIGENDIAN + std::swap(*((uint32*)(&spell->SpellFamilyFlags)),*(((uint32*)(&spell->SpellFamilyFlags))+1)); + #endif + } + + for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); + + if(!skillLine) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); + + if(spellInfo && (spellInfo->Attributes & 0x1D0) == 0x1D0) + { + for (unsigned int i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i) + { + CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i); + if(!cFamily) + continue; + + if(skillLine->skillId != cFamily->skillLine && skillLine->skillId != cFamily->skillLine2) + continue; + + sPetFamilySpellsStore[i].insert(spellInfo->Id); + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellCastTimesStore, dbcPath,"SpellCastTimes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDurationStore, dbcPath,"SpellDuration.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellFocusObjectStore, dbcPath,"SpellFocusObject.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentStore,dbcPath,"SpellItemEnchantment.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentConditionStore,dbcPath,"SpellItemEnchantmentCondition.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRadiusStore, dbcPath,"SpellRadius.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRangeStore, dbcPath,"SpellRange.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc"); + + // create talent spells set + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + for (int j = 0; j < 5; j++) + if(talentInfo->RankID[j]) + sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j); + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc"); + + // preper fast data access to bit pos of talent ranks for use at inspecting + { + // fill table by amount of talent ranks and fill sTalentTabBitSizeInInspect + // store in with (row,col,talent)->size key for correct sorting by (row,col) + typedef std::map<uint32,uint32> TalentBitSize; + TalentBitSize sTalentBitSize; + for(uint32 i = 1; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + if(!talentTabInfo) + continue; + + // find talent rank + uint32 curtalent_maxrank = 0; + for(uint32 k = 5; k > 0; --k) + { + if(talentInfo->RankID[k-1]) + { + curtalent_maxrank = k; + break; + } + } + + sTalentBitSize[(talentInfo->Row<<24) + (talentInfo->Col<<16)+talentInfo->TalentID] = curtalent_maxrank; + sTalentTabSizeInInspect[talentInfo->TalentTab] += curtalent_maxrank; + } + + // now have all max ranks (and then bit amount used for store talent ranks in inspect) + for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) + { + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId ); + if(!talentTabInfo) + continue; + + // store class talent tab pages + uint32 cls = 1; + for(uint32 m=1;!(m & talentTabInfo->ClassMask) && cls < 12 /*MAX_CLASSES*/;m <<=1, ++cls) {} + + sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; + + // add total amount bits for first rank starting from talent tab first talent rank pos. + uint32 pos = 0; + for(TalentBitSize::iterator itr = sTalentBitSize.begin(); itr != sTalentBitSize.end(); ++itr) + { + uint32 talentId = itr->first & 0xFFFF; + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId ); + if(!talentInfo) + continue; + + if(talentInfo->TalentTab != talentTabId) + continue; + + sTalentPosInInspect[talentId] = pos; + pos+= itr->second; + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc"); + + // Initialize global taxinodes mask + memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); + for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) + { + if(sTaxiNodesStore.LookupEntry(i)) + { + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + sTaxiNodesMask[field] |= submask; + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathStore, dbcPath,"TaxiPath.dbc"); + for(uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) + if(TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) + sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID,entry->price); + uint32 pathCount = sTaxiPathStore.GetNumRows(); + + //## TaxiPathNode.dbc ## Loaded only for initialization different structures + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathNodeStore, dbcPath,"TaxiPathNode.dbc"); + // Calculate path nodes count + std::vector<uint32> pathLength; + pathLength.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + ++pathLength[entry->path]; + // Set path length + sTaxiPathNodesByPath.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i) + sTaxiPathNodesByPath[i].resize(pathLength[i]); + // fill data + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + sTaxiPathNodesByPath[entry->path][entry->index] = TaxiPathNode(entry->mapid,entry->x,entry->y,entry->z,entry->actionFlag,entry->delay); + sTaxiPathNodeStore.Clear(); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc"); + + // error checks + if(bad_dbc_files.size() >= DBCFilesCount ) + { + sLog.outError("\nIncorrect DataDir value in mangosd.conf or ALL required *.dbc files (%d) not found by path: %sdbc",DBCFilesCount,dataPath.c_str()); + exit(1); + } + else if(!bad_dbc_files.empty() ) + { + std::string str; + for(std::list<std::string>::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i) + str += *i + "\n"; + + sLog.outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s",bad_dbc_files.size(),DBCFilesCount,str.c_str()); + exit(1); + } + + // check at up-to-date DBC files (53085 is last added spell in 2.4.3) + // check at up-to-date DBC files (17514 is last ID in SkillLineAbilities in 2.4.3) + // check at up-to-date DBC files (598 is last map added in 2.4.3) + // check at up-to-date DBC files (1127 is last gem property added in 2.4.3) + // check at up-to-date DBC files (2425 is last item extended cost added in 2.4.3) + // check at up-to-date DBC files (71 is last char title added in 2.4.3) + // check at up-to-date DBC files (1768 is last area added in 2.4.3) + if( !sSpellStore.LookupEntry(53085) || + !sSkillLineAbilityStore.LookupEntry(17514) || + !sMapStore.LookupEntry(598) || + !sGemPropertiesStore.LookupEntry(1127) || + !sItemExtendedCostStore.LookupEntry(2425) || + !sCharTitlesStore.LookupEntry(71) || + !sAreaStore.LookupEntry(1768) ) + { + sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client."); + exit(1); + } + + sLog.outString(); + sLog.outString( ">> Loaded %d data stores", DBCFilesCount ); + sLog.outString(); +} + +SimpleFactionsList const* GetFactionTeamList(uint32 faction) +{ + FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction); + if(itr==sFactionTeamMap.end()) + return NULL; + return &itr->second; +} + +char* GetPetName(uint32 petfamily, uint32 dbclang) +{ + if(!petfamily) + return NULL; + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily); + if(!pet_family) + return NULL; + return pet_family->Name[dbclang]?pet_family->Name[dbclang]:NULL; +} + +TalentSpellPos const* GetTalentSpellPos(uint32 spellId) +{ + TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId); + if(itr==sTalentSpellPosMap.end()) + return NULL; + + return &itr->second; +} + +uint32 GetTalentSpellCost(uint32 spellId) +{ + if(TalentSpellPos const* pos = GetTalentSpellPos(spellId)) + return pos->rank+1; + + return 0; +} + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) +{ + AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); + if(i == sAreaFlagByAreaID.end()) + return NULL; + + return sAreaStore.LookupEntry(i->second); +} + +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id) +{ + if(area_flag) + return sAreaStore.LookupEntry(area_flag); + + if(MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) + return GetAreaEntryByAreaID(mapEntry->linked_zone); + + return NULL; +} + +uint32 GetAreaFlagByMapId(uint32 mapid) +{ + AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); + if(i == sAreaFlagByMapID.end()) + return 0; + else + return i->second; +} + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) +{ + if(mapid != 530) // speed for most cases + return mapid; + + if(WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId)) + return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id; + + return mapid; +} + +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId) +{ + mapid = GetVirtualMapForMapAndZone(mapid,zoneId); + if(mapid < 2) + return CONTENT_1_60; + + MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); + return (!mapEntry || !mapEntry->IsExpansionMap()) ? CONTENT_1_60 : CONTENT_61_70; +} + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) +{ + // not sorted, numbering index from 0 + for(uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); + if(ch && ch->ChannelID == channel_id) + return ch; + } + return NULL; +} + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +{ + if(requiredTotemCategoryId==0) + return true; + if(itemTotemCategoryId==0) + return false; + + TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); + if(!itemEntry) + return false; + TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); + if(!reqEntry) + return false; + + if(itemEntry->categoryType!=reqEntry->categoryType) + return false; + + return (itemEntry->categoryMask & reqEntry->categoryMask)==reqEntry->categoryMask; +} + +void Zone2MapCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + std::swap(x,y); // at client map coords swapped + x = x*((maEntry->x2-maEntry->x1)/100)+maEntry->x1; + y = y*((maEntry->y2-maEntry->y1)/100)+maEntry->y1; // client y coord from top to down +} + +void Map2ZoneCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); + y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down + std::swap(x,y); // client have map coords swapped +} + +uint32 GetTalentInspectBitPosInTab(uint32 talentId) +{ + TalentInspectMap::const_iterator itr = sTalentPosInInspect.find(talentId); + if(itr == sTalentPosInInspect.end()) + return 0; + + return itr->second; +} + +uint32 GetTalentTabInspectBitSize(uint32 talentTabId) +{ + TalentInspectMap::const_iterator itr = sTalentTabSizeInInspect.find(talentTabId); + if(itr == sTalentTabSizeInInspect.end()) + return 0; + + return itr->second; +} + +uint32 const* GetTalentTabPages(uint32 cls) +{ + return sTalentTabPages[cls]; +} + +// script support functions +MANGOS_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore() { return &sSoundEntriesStore; } +MANGOS_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore() { return &sSpellStore; } +MANGOS_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore() { return &sSpellRangeStore; } diff --git a/src/shared/Database/DBCStores.h b/src/shared/Database/DBCStores.h new file mode 100644 index 00000000000..e1d05298695 --- /dev/null +++ b/src/shared/Database/DBCStores.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTORES_H +#define DBCSTORES_H + +#include "Common.h" +//#include "DataStore.h" +#include "dbcfile.h" +#include "DBCStructure.h" + +#include <list> + +typedef std::list<uint32> SimpleFactionsList; + +SimpleFactionsList const* GetFactionTeamList(uint32 faction); +char* GetPetName(uint32 petfamily, uint32 dbclang); +uint32 GetTalentSpellCost(uint32 spellId); +TalentSpellPos const* GetTalentSpellPos(uint32 spellId); + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); +uint32 GetAreaFlagByMapId(uint32 mapid); + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); + +enum ContentLevels +{ + CONTENT_1_60 = 0, + CONTENT_61_70 +}; +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id); + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); + +void Zone2MapCoordinates(float& x,float& y,uint32 zone); +void Map2ZoneCoordinates(float& x,float& y,uint32 zone); + +uint32 GetTalentInspectBitPosInTab(uint32 talentId); +uint32 GetTalentTabInspectBitSize(uint32 talentTabId); +uint32 const* /*[3]*/ GetTalentTabPages(uint32 cls); + +template<class T> +class DBCStorage +{ + typedef std::list<char*> StringPoolList; + public: + explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } + ~DBCStorage() { Clear(); } + + T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } + uint32 GetNumRows() const { return nCount; } + char const* GetFormat() const { return fmt; } + uint32 GetFieldCount() const { return fieldCount; } + + bool Load(char const* fn) + { + + DBCFile dbc; + // Check if load was sucessful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + fieldCount = dbc.GetCols(); + m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable); + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + // error in dbc file at loading if NULL + return indexTable!=NULL; + } + + bool LoadStringsFrom(char const* fn) + { + // DBC must be already loaded using Load + if(!indexTable) + return false; + + DBCFile dbc; + // Check if load was successful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + return true; + } + + void Clear() + { + if (!indexTable) + return; + + delete[] ((char*)indexTable); + indexTable = NULL; + delete[] ((char*)m_dataTable); + m_dataTable = NULL; + + while(!m_stringPoolList.empty()) + { + delete[] m_stringPoolList.front(); + m_stringPoolList.pop_front(); + } + nCount = 0; + } + + private: + uint32 nCount; + uint32 fieldCount; + char const* fmt; + T** indexTable; + T* m_dataTable; + StringPoolList m_stringPoolList; +}; + +extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions +extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore; +extern DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore; +extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore; +//extern DBCStorage <ChatChannelsEntry> sChatChannelsStore; -- accessed using function, no usable index +extern DBCStorage <CharTitlesEntry> sCharTitlesStore; +extern DBCStorage <ChrClassesEntry> sChrClassesStore; +extern DBCStorage <ChrRacesEntry> sChrRacesStore; +extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore; +extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore; +extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore; +extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore; +extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore; +extern DBCStorage <EmotesTextEntry> sEmotesTextStore; +extern DBCStorage <FactionEntry> sFactionStore; +extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore; +extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore; + +extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore; +extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore; +extern DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore; +extern DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore; +extern DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore; +extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore; +//extern DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore; -- not used currently +extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore; +extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore; +extern DBCStorage <ItemEntry> sItemStore; +//extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; -- not used currently +extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore; +extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore; +extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore; +extern DBCStorage <ItemSetEntry> sItemSetStore; +extern DBCStorage <LockEntry> sLockStore; +extern DBCStorage <MailTemplateEntry> sMailTemplateStore; +extern DBCStorage <MapEntry> sMapStore; +extern DBCStorage <QuestSortEntry> sQuestSortStore; +extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore; +extern DBCStorage <SkillLineEntry> sSkillLineStore; +extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore; +extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore; +extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore; +extern DBCStorage <SpellDurationEntry> sSpellDurationStore; +extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore; +extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore; +extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore; +extern SpellCategoryStore sSpellCategoryStore; +extern PetFamilySpellsStore sPetFamilySpellsStore; +extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore; +extern DBCStorage <SpellRangeEntry> sSpellRangeStore; +extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore; +extern DBCStorage <SpellEntry> sSpellStore; +extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore; +extern DBCStorage <TalentEntry> sTalentStore; +extern DBCStorage <TalentTabEntry> sTalentTabStore; +extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore; +extern DBCStorage <TaxiPathEntry> sTaxiPathStore; +extern TaxiMask sTaxiNodesMask; +extern TaxiPathSetBySource sTaxiPathSetBySource; +extern TaxiPathNodesByPath sTaxiPathNodesByPath; +extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore; +//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates +extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore; + +void LoadDBCStores(std::string dataPath); + +// script support functions +MANGOS_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore(); +MANGOS_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore(); +MANGOS_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore(); +#endif diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h new file mode 100644 index 00000000000..c0370180511 --- /dev/null +++ b/src/shared/Database/DBCStructure.h @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTRUCTURE_H +#define DBCSTRUCTURE_H + +#include "Platform/Define.h" + +#include <map> +#include <set> +#include <vector> + +// Structures using to access raw DBC data and required packing to portability + +// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack(1) +#else +#pragma pack(push,1) +#endif + +enum AreaTeams +{ + AREATEAM_NONE = 0, + AREATEAM_ALLY = 2, + AREATEAM_HORDE = 4 +}; + +enum AreaFlags +{ + AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) + AREA_FLAG_UNK1 = 0x00000002, // unknown, (only Naxxramas and Razorfen Downs) + AREA_FLAG_UNK2 = 0x00000004, // Only used on development map + AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // slave capital city flag? + AREA_FLAG_UNK3 = 0x00000010, // unknown + AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? + AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag + AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas + AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag + AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) + AREA_FLAG_OUTLAND = 0x00000400, // outland zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) + AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) + AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway + AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 2.4.2) + AREA_FLAG_OUTLAND2 = 0x00004000, // outland zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) + AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) + AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only + AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 2.4.2) + AREA_FLAG_UNK5 = 0x00040000, // just used for Amani Pass, Hatchet Hills + AREA_FLAG_LOWLEVEL = 0x00100000 // used for some starting areas with area_level <=15 +}; + +enum FactionTemplateFlags +{ + FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats +}; + +struct AreaTableEntry +{ + uint32 ID; // 0 + uint32 mapid; // 1 + uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area + uint32 exploreFlag; // 3, main index + uint32 flags; // 4, unknown value but 312 for all cities + // 5-9 unused + int32 area_level; // 10 + char* area_name[16]; // 11-26 + // 27, string flags, unused + uint32 team; // 28 +}; + +struct AreaTriggerEntry +{ + uint32 id; // 0 + uint32 mapid; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + float radius; // 5 + float box_x; // 6 extent x edge + float box_y; // 7 extent y edge + float box_z; // 8 extent z edge + float box_orientation; // 9 extent rotation by about z axis +}; + +struct BankBagSlotPricesEntry +{ + uint32 ID; + uint32 price; +}; + +struct BattlemasterListEntry +{ + uint32 id; // 0 + uint32 mapid[3]; // 1-3 mapid + // 4-8 unused + uint32 type; // 9 (3 - BG, 4 - arena) + uint32 minlvl; // 10 + uint32 maxlvl; // 11 + uint32 maxplayersperteam; // 12 + // 13-14 unused + char* name[16]; // 15-30 + // 31 string flag, unused + // 32 unused +}; + +struct CharTitlesEntry +{ + uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId() + //uint32 unk1; // 1 flags? + //char* name[16]; // 2-17, unused + // 18 string flag, unused + //char* name2[16]; // 19-34, unused + // 35 string flag, unused + uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES +}; + +struct ChatChannelsEntry +{ + uint32 ChannelID; // 0 + uint32 flags; // 1 + char* pattern[16]; // 3-18 + // 19 string flags, unused + //char* name[16]; // 20-35 unused + // 36 string flag, unused +}; + +struct ChrClassesEntry +{ + uint32 ClassID; // 0 + // 1-2, unused + uint32 powerType; // 3 + // 4, unused + //char* name[16]; // 5-20 unused + // 21 string flag, unused + //char* string1[16]; // 21-36 unused + // 37 string flag, unused + //char* string2[16]; // 38-53 unused + // 54 string flag, unused + // 55, unused + uint32 spellfamily; // 56 + // 57, unused +}; + +struct ChrRacesEntry +{ + uint32 RaceID; // 0 + // 1 unused + uint32 FactionID; // 2 facton template id + // 3 unused + uint32 model_m; // 4 + uint32 model_f; // 5 + // 6-7 unused + uint32 TeamID; // 8 (7-Alliance 1-Horde) + // 9-12 unused + uint32 startmovie; // 13 id from CinematicCamera.dbc + char* name[16]; // 14-29 used for DBC language detection/selection + // 30 string flags, unused + //char* string1[16]; // 31-46 used for DBC language detection/selection + // 47 string flags, unused + //char* string2[16]; // 48-63 used for DBC language detection/selection + // 64 string flags, unused + // 65-67 unused + //uint32 addon // 68 (0 - original race, 1 - tbc addon, ...) unused +}; + +struct CreatureDisplayInfoEntry +{ + uint32 Displayid; // 0 + // 1-3,unused + float scale; // 4 + // 5-13,unused +}; + +struct CreatureFamilyEntry +{ + uint32 ID; // 0 + float minScale; // 1 + uint32 minScaleLevel; // 2 0/1 + float maxScale; // 3 + uint32 maxScaleLevel; // 4 0/60 + uint32 skillLine; // 5 + uint32 skillLine2; // 6 + uint32 petFoodMask; // 7 + char* Name[16]; // 8-23 + // 24 string flags, unused + // 25 icon, unused +}; + +struct CreatureSpellDataEntry +{ + uint32 ID; // 0 + //uint32 spellId[4]; // 1-4 hunter pet learned spell (for later use) +}; + +struct DurabilityCostsEntry +{ + uint32 Itemlvl; // 0 + uint32 multiplier[29]; // 1-29 +}; + +struct DurabilityQualityEntry +{ + uint32 Id; // 0 + float quality_mod; // 1 +}; + +struct EmotesTextEntry +{ + uint32 Id; + uint32 textid; +}; + +struct FactionEntry +{ + uint32 ID; // 0 + int32 reputationListID; // 1 + uint32 BaseRepRaceMask[4]; // 2-5 Base reputation race masks (see enum Races) + uint32 BaseRepClassMask[4]; // 6-9 Base reputation class masks (see enum Classes) + int32 BaseRepValue[4]; // 10-13 Base reputation values + uint32 ReputationFlags[4]; // 14-17 Default flags to apply + uint32 team; // 18 enum Team + char* name[16]; // 19-34 + // 35 string flags, unused + //char* description[16]; // 36-51 unused + // 52 string flags, unused +}; + +enum FactionMasks +{ + FACTION_MASK_PLAYER = 1, // any player + FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team + FACTION_MASK_HORDE = 4, // player or creature from horde team + FACTION_MASK_MONSTER = 8 // aggressive creature from monster team + // if none flags set then non-aggressive creature +}; + +struct FactionTemplateEntry +{ + uint32 ID; // 0 + uint32 faction; // 1 + uint32 factionFlags; // 2 specific flags for that faction + uint32 ourMask; // 3 if mask set (see FactionMasks) then faction included in masked team + uint32 friendlyMask; // 4 if mask set (see FactionMasks) then faction friendly to masked team + uint32 hostileMask; // 5 if mask set (see FactionMasks) then faction hostile to masked team + uint32 enemyFaction1; // 6 + uint32 enemyFaction2; // 7 + uint32 enemyFaction3; // 8 + uint32 enemyFaction4; // 9 + uint32 friendFaction1; // 10 + uint32 friendFaction2; // 11 + uint32 friendFaction3; // 12 + uint32 friendFaction4; // 13 + //------------------------------------------------------- end structure + + // helpers + bool IsFriendlyTo(FactionTemplateEntry const& entry) const + { + if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction ) + return false; + if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction ) + return true; + return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask); + } + bool IsHostileTo(FactionTemplateEntry const& entry) const + { + if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction ) + return true; + if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction ) + return false; + return (hostileMask & entry.ourMask) != 0; + } + bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; } + bool IsNeutralToAll() const { return hostileMask == 0 && friendlyMask == 0 && enemyFaction1==0 && enemyFaction2==0 && enemyFaction3==0 && enemyFaction4==0; } + bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; } +}; + +struct GemPropertiesEntry +{ + uint32 ID; + uint32 spellitemenchantement; + uint32 color; +}; + +#define GT_MAX_LEVEL 100 +struct GtCombatRatingsEntry +{ + float ratio; +}; + +struct GtChanceToMeleeCritBaseEntry +{ + float base; +}; + +struct GtChanceToMeleeCritEntry +{ + float ratio; +}; + +struct GtChanceToSpellCritBaseEntry +{ + float base; +}; + +struct GtChanceToSpellCritEntry +{ + float ratio; +}; + +struct GtOCTRegenHPEntry +{ + float ratio; +}; + +//struct GtOCTRegenMPEntry +//{ +// float ratio; +//}; + +struct GtRegenHPPerSptEntry +{ + float ratio; +}; + +struct GtRegenMPPerSptEntry +{ + float ratio; +}; + +struct ItemEntry +{ + uint32 ID; + uint32 DisplayId; + uint32 InventoryType; + uint32 Sheath; +}; + +struct ItemDisplayInfoEntry +{ + uint32 ID; + uint32 randomPropertyChance; +}; + +//struct ItemCondExtCostsEntry +//{ +// uint32 ID; +// uint32 condExtendedCost; // ItemPrototype::CondExtendedCost +// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost +// uint32 arenaseason; // arena season number(1-4) +//}; + +struct ItemExtendedCostEntry +{ + uint32 ID; // 0 extended-cost entry id + uint32 reqhonorpoints; // 1 required honor points + uint32 reqarenapoints; // 2 required arena points + uint32 reqitem[5]; // 3-7 required item id + uint32 reqitemcount[5]; // 8-12 required count of 1st item + uint32 reqpersonalarenarating; // 13 required personal arena rating +}; + +struct ItemRandomPropertiesEntry +{ + uint32 ID; // 0 + //char* internalName // 1 unused + uint32 enchant_id[3]; // 2-4 + // 5-6 unused, 0 only values, reserved for additional enchantments? + //char* nameSuffix[16] // 7-22, unused + // 23 nameSufix flags, unused +}; + +struct ItemRandomSuffixEntry +{ + uint32 ID; // 0 + //char* name[16] // 1-16 unused + // 17, name flags, unused + // 18 unused + uint32 enchant_id[3]; // 19-21 + uint32 prefix[3]; // 22-24 +}; + +struct ItemSetEntry +{ + //uint32 id // 0 item set ID + char* name[16]; // 1-16 + // 17 string flags, unused + // 18-28 items from set, but not have all items listed, use ItemPrototype::ItemSet instead + // 29-34 unused + uint32 spells[8]; // 35-42 + uint32 items_to_triggerspell[8]; // 43-50 + uint32 required_skill_id; // 51 + uint32 required_skill_value; // 52 +}; + +struct LockEntry +{ + uint32 ID; // 0 + uint32 keytype[5]; // 1-5 + // 6-8, not used + uint32 key[5]; // 9-13 + // 14-16, not used + uint32 requiredminingskill; // 17 + uint32 requiredlockskill; // 18 + // 19-32, not used +}; + +struct MailTemplateEntry +{ + uint32 ID; // 0 + //char* subject[16]; // 1-16 + // 17 name flags, unused + //char* content[16]; // 18-33 +}; + +enum MapTypes +{ + MAP_COMMON = 0, + MAP_INSTANCE = 1, + MAP_RAID = 2, + MAP_BATTLEGROUND = 3, + MAP_ARENA = 4 +}; + +struct MapEntry +{ + uint32 MapID; // 0 + //char* internalname; // 1 unused + uint32 map_type; // 2 + // 3 unused + char* name[16]; // 4-19 + // 20 name flags, unused + // 21-23 unused (something PvPZone related - levels?) + // 24-26 + uint32 linked_zone; // 27 common zone for instance and continent map + //char* hordeIntro // 28-43 text for PvP Zones + // 44 intro text flags + //char* allianceIntro // 45-60 text for PvP Zones + // 46 intro text flags + // 47-61 not used + uint32 multimap_id; // 62 + // 63-65 not used + //chat* unknownText1 // 66-81 unknown empty text fields, possible normal Intro text. + // 82 text flags + //chat* heroicIntroText // 83-98 heroic mode requirement text + // 99 text flags + //chat* unknownText2 // 100-115 unknown empty text fields + // 116 text flags + int32 parent_map; // 117 map_id of parent map + //float start_x // 118 enter x coordinate (if exist single entry) + //float start_y // 119 enter y coordinate (if exist single entry) + uint32 resetTimeRaid; // 120 + uint32 resetTimeHeroic; // 121 + // 122-123 + uint32 addon; // 124 (0-original maps,1-tbc addon) + + // Helpers + bool IsExpansionMap() const { return addon != 0; } + bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable + bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + bool IsRaid() const { return map_type == MAP_RAID; } + bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; } + bool IsBattleArena() const { return map_type == MAP_ARENA; } + bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; } + bool SupportsHeroicMode() const { return resetTimeHeroic && !resetTimeRaid; } + bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; } + + bool IsMountAllowed() const + { + return !IsDungeon() || + MapID==568 || MapID==309 || MapID==209 || MapID==534 || + MapID==560 || MapID==509 || MapID==269; + } +}; + +struct QuestSortEntry +{ + uint32 id; // 0, sort id + //char* name[16]; // 1-16, unused + // 17 name flags, unused +}; + +struct RandomPropertiesPointsEntry +{ + //uint32 Id; // 0 hidden key + uint32 itemLevel; // 1 + uint32 EpicPropertiesPoints[5]; // 2-6 + uint32 RarePropertiesPoints[5]; // 7-11 + uint32 UncommonPropertiesPoints[5]; // 12-16 +}; + +//struct SkillLineCategoryEntry{ +// uint32 id; // 0 hidden key +// char* name[16]; // 1 - 17 Category name +// // 18 string flag +// uint32 displayOrder; // Display order in character tab +//}; + +//struct SkillRaceClassInfoEntry{ +// uint32 id; // 0 +// uint32 skillId; // 1 present some refrences to unknown skill +// uint32 raceMask; // 2 +// uint32 classMask; // 3 +// uint32 flags; // 4 mask for some thing +// uint32 reqLevel; // 5 +// uint32 skillTierId; // 6 +// uint32 skillCostID; // 7 +//}; + +//struct SkillTiersEntry{ +// uint32 id; // 0 +// uint32 skillValue[16]; // 1-17 unknown possibly add value on learn? +// uint32 maxSkillValue[16]; // Max value for rank +//}; + +struct SkillLineEntry +{ + uint32 id; // 0 + uint32 categoryId; // 1 (index from SkillLineCategory.dbc) + //uint32 skillCostID; // 2 not used + char* name[16]; // 3-18 + // 19 string flags, not used + //char* description[16]; // 20-35, not used + // 36 string flags, not used + uint32 spellIcon; // 37 +}; + +enum AbilytyLearnType +{ + ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, + ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 +}; + +struct SkillLineAbilityEntry +{ + uint32 id; // 0, INDEX + uint32 skillId; // 1 + uint32 spellId; // 2 + uint32 racemask; // 3 + uint32 classmask; // 4 + //uint32 racemaskNot; // 5 always 0 in 2.4.2 + //uint32 classmaskNot; // 6 always 0 in 2.4.2 + uint32 req_skill_value; // 7 for trade skill.not for training. + uint32 forward_spellid; // 8 + uint32 learnOnGetSkill; // 9 can be 1 or 2 for spells learned on get skill + uint32 max_value; // 10 + uint32 min_value; // 11 + // 12-13, unknown, always 0 + uint32 reqtrainpoints; // 14 +}; + +struct SoundEntriesEntry +{ + uint32 Id; // 0, sound id + //uint32 Type; // 1, sound type (10 generally for creature, etc) + //char* InternalName; // 2, internal name, for use in lookup command for example + //char* FileName[10]; // 3-12, file names + //uint32 Unk13[10]; // 13-22, linked with file names? + //char* Path; // 23 + // 24-28, unknown +}; + +struct SpellEntry +{ + uint32 Id; // 0 normally counted from 0 field (but some tools start counting from 1, check this before tool use for data view!) + uint32 Category; // 1 + //uint32 castUI // 2 not used + uint32 Dispel; // 3 + uint32 Mechanic; // 4 + uint32 Attributes; // 5 + uint32 AttributesEx; // 6 + uint32 AttributesEx2; // 7 + uint32 AttributesEx3; // 8 + uint32 AttributesEx4; // 9 + uint32 AttributesEx5; // 10 + //uint32 AttributesEx6; // 11 not used + uint32 Stances; // 12 + uint32 StancesNot; // 13 + uint32 Targets; // 14 + uint32 TargetCreatureType; // 15 + uint32 RequiresSpellFocus; // 16 + //uint32 FacingCasterFlags; // 17 not used + uint32 CasterAuraState; // 18 + uint32 TargetAuraState; // 19 + uint32 CasterAuraStateNot; // 20 + uint32 TargetAuraStateNot; // 21 + uint32 CastingTimeIndex; // 22 + uint32 RecoveryTime; // 23 + uint32 CategoryRecoveryTime; // 24 + uint32 InterruptFlags; // 25 + uint32 AuraInterruptFlags; // 26 + uint32 ChannelInterruptFlags; // 27 + uint32 procFlags; // 28 + uint32 procChance; // 29 + uint32 procCharges; // 30 + uint32 maxLevel; // 31 + uint32 baseLevel; // 32 + uint32 spellLevel; // 33 + uint32 DurationIndex; // 34 + uint32 powerType; // 35 + uint32 manaCost; // 36 + uint32 manaCostPerlevel; // 37 + uint32 manaPerSecond; // 38 + uint32 manaPerSecondPerLevel; // 39 + uint32 rangeIndex; // 40 + float speed; // 41 + //uint32 modalNextSpell; // 42 + uint32 StackAmount; // 43 + uint32 Totem[2]; // 44-45 + int32 Reagent[8]; // 46-53 + uint32 ReagentCount[8]; // 54-61 + int32 EquippedItemClass; // 62 (value) + int32 EquippedItemSubClassMask; // 63 (mask) + int32 EquippedItemInventoryTypeMask; // 64 (mask) + uint32 Effect[3]; // 65-67 + int32 EffectDieSides[3]; // 68-70 + uint32 EffectBaseDice[3]; // 71-73 + float EffectDicePerLevel[3]; // 74-76 + float EffectRealPointsPerLevel[3]; // 77-79 + int32 EffectBasePoints[3]; // 80-82 (don't must be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints) + uint32 EffectMechanic[3]; // 83-85 + uint32 EffectImplicitTargetA[3]; // 86-88 + uint32 EffectImplicitTargetB[3]; // 89-91 + uint32 EffectRadiusIndex[3]; // 92-94 - spellradius.dbc + uint32 EffectApplyAuraName[3]; // 95-97 + uint32 EffectAmplitude[3]; // 98-100 + float EffectMultipleValue[3]; // 101-103 + uint32 EffectChainTarget[3]; // 104-106 + uint32 EffectItemType[3]; // 107-109 + int32 EffectMiscValue[3]; // 110-112 + int32 EffectMiscValueB[3]; // 113-115 + uint32 EffectTriggerSpell[3]; // 116-118 + float EffectPointsPerComboPoint[3]; // 119-121 + uint32 SpellVisual; // 122 + // 123 not used + uint32 SpellIconID; // 124 + uint32 activeIconID; // 125 + //uint32 spellPriority; // 126 + char* SpellName[16]; // 127-142 + //uint32 SpellNameFlag; // 143 + char* Rank[16]; // 144-159 + //uint32 RankFlags; // 160 + //char* Description[16]; // 161-176 not used + //uint32 DescriptionFlags; // 177 not used + //char* ToolTip[16]; // 178-193 not used + //uint32 ToolTipFlags; // 194 not used + uint32 ManaCostPercentage; // 195 + uint32 StartRecoveryCategory; // 196 + uint32 StartRecoveryTime; // 197 + uint32 MaxTargetLevel; // 198 + uint32 SpellFamilyName; // 199 + uint64 SpellFamilyFlags; // 200+201 + uint32 MaxAffectedTargets; // 202 + uint32 DmgClass; // 203 defenseType + uint32 PreventionType; // 204 + //uint32 StanceBarOrder; // 205 not used + float DmgMultiplier[3]; // 206-208 + //uint32 MinFactionId; // 209 not used, and 0 in 2.4.2 + //uint32 MinReputation; // 210 not used, and 0 in 2.4.2 + //uint32 RequiredAuraVision; // 211 not used + uint32 TotemCategory[2]; // 212-213 + uint32 AreaId; // 214 + uint32 SchoolMask; // 215 school mask + + private: + // prevent creating custom entries (copy data from original in fact) + SpellEntry(SpellEntry const&); // DON'T must have implementation +}; + +typedef std::set<uint32> SpellCategorySet; +typedef std::map<uint32,SpellCategorySet > SpellCategoryStore; +typedef std::set<uint32> PetFamilySpellsSet; +typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore; + +struct SpellCastTimesEntry +{ + uint32 ID; // 0 + int32 CastTime; // 1 + //float CastTimePerLevel; // 2 unsure / per skill? + //int32 MinCastTime; // 3 unsure +}; + +struct SpellFocusObjectEntry +{ + uint32 ID; // 0 + //char* Name[16]; // 1-15 unused + // 16 string flags, unused +}; + +// stored in SQL table +struct SpellThreatEntry +{ + uint32 spellId; + int32 threat; +}; + +struct SpellRadiusEntry +{ + uint32 ID; + float Radius; + float Radius2; +}; + +struct SpellRangeEntry +{ + uint32 ID; + float minRange; + float maxRange; +}; + +struct SpellShapeshiftEntry +{ + uint32 ID; // 0 + //uint32 buttonPosition; // 1 unused + //char* Name[16]; // 2-17 unused + //uint32 NameFlags; // 18 unused + uint32 flags1; // 19 + int32 creatureType; // 20 <=0 humanoid, other normal creature types + //uint32 unk1; // 21 unused + uint32 attackSpeed; // 22 + //uint32 modelID; // 23 unused, alliance modelid (where horde case?) + //uint32 unk2; // 24 unused + //uint32 unk3; // 25 unused + //uint32 unk4; // 26 unused + //uint32 unk5; // 27 unused + //uint32 unk6; // 28 unused + //uint32 unk7; // 29 unused + //uint32 unk8; // 30 unused + //uint32 unk9; // 31 unused + //uint32 unk10; // 32 unused + //uint32 unk11; // 33 unused + //uint32 unk12; // 34 unused +}; + +struct SpellDurationEntry +{ + uint32 ID; + int32 Duration[3]; +}; + +enum ItemEnchantmentType +{ + ITEM_ENCHANTMENT_TYPE_NONE = 0, + ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1, + ITEM_ENCHANTMENT_TYPE_DAMAGE = 2, + ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3, + ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4, + ITEM_ENCHANTMENT_TYPE_STAT = 5, + ITEM_ENCHANTMENT_TYPE_TOTEM = 6 +}; + +struct SpellItemEnchantmentEntry +{ + uint32 ID; // 0 + uint32 type[3]; // 1-3 + uint32 amount[3]; // 4-6 + //uint32 amount2[3] // 7-9 always same as similar `amount` value + uint32 spellid[3]; // 10-12 + char* description[16]; // 13-29 + // 30 description flags + uint32 aura_id; // 31 + uint32 slot; // 32 + uint32 GemID; // 33 + uint32 EnchantmentCondition; // 34 +}; + +struct SpellItemEnchantmentConditionEntry +{ + uint32 ID; + uint8 Color[5]; + uint8 Comparator[5]; + uint8 CompareColor[5]; + uint32 Value[5]; +}; + +struct StableSlotPricesEntry +{ + uint32 Slot; + uint32 Price; +}; + +struct TalentEntry +{ + uint32 TalentID; // 0 + uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry) + uint32 Row; // 2 + uint32 Col; // 3 + uint32 RankID[5]; // 4-8 + // 9-12 not used, always 0, maybe not used high ranks + uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry) + // 14-15 not used + uint32 DependsOnRank; // 16 + // 17-19 not used + uint32 DependsOnSpell; // 20 req.spell +}; + +struct TalentTabEntry +{ + uint32 TalentTabID; // 0 + //char* name[16]; // 1-16, unused + //uint32 nameFlags; // 17, unused + //unit32 spellicon; // 18 + // 19 not used + uint32 ClassMask; // 20 + uint32 tabpage; // 21 + //char* internalname; // 22 +}; + +struct TaxiPathEntry +{ + uint32 ID; + uint32 from; + uint32 to; + uint32 price; +}; + +struct TaxiNodesEntry +{ + uint32 ID; // 0 + uint32 map_id; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + //char* name[16]; // 5-21 + // 22 string flags, unused + uint32 horde_mount_type; // 23 + uint32 alliance_mount_type; // 24 +}; + +enum TotemCategoryType +{ + TOTEM_CATEGORY_TYPE_KNIFE = 1, + TOTEM_CATEGORY_TYPE_TOTEM = 2, + TOTEM_CATEGORY_TYPE_ROD = 3, + TOTEM_CATEGORY_TYPE_PICK = 21, + TOTEM_CATEGORY_TYPE_STONE = 22, + TOTEM_CATEGORY_TYPE_HAMMER = 23, + TOTEM_CATEGORY_TYPE_SPANNER = 24 +}; + +struct TotemCategoryEntry +{ + uint32 ID; // 0 + //char* name[16]; // 1-16 + // 17 string flags, unused + uint32 categoryType; // 18 (one for specialization) + uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods) +}; + +struct WorldMapAreaEntry +{ + //uint32 ID; // 0 + uint32 map_id; // 1 + uint32 area_id; // 2 index (continent 0 areas ignored) + //char* internal_name // 3 + float y1; // 4 + float y2; // 5 + float x1; // 6 + float x2; // 7 + int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally) +}; + +struct WorldSafeLocsEntry +{ + uint32 ID; // 0 + uint32 map_id; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + //char* name[16] // 5-20 name, unused + // 21 name flags, unused +}; + +// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack() +#else +#pragma pack(pop) +#endif + +// Structures not used for casting to loaded DBC data and not required then packing +struct TalentSpellPos +{ + TalentSpellPos() : talent_id(0), rank(0) {} + TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {} + + uint16 talent_id; + uint8 rank; +}; + +typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap; + +struct TaxiPathBySourceAndDestination +{ + TaxiPathBySourceAndDestination() : ID(0),price(0) {} + TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {} + + uint32 ID; + uint32 price; +}; +typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource; +typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource; + +struct TaxiPathNode +{ + TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {} + TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {} + + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +typedef std::vector<TaxiPathNode> TaxiPathNodeList; +typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath; + +#define TaxiMaskSize 16 +typedef uint32 TaxiMask[TaxiMaskSize]; +#endif diff --git a/src/shared/Database/DBCfmt.cpp b/src/shared/Database/DBCfmt.cpp new file mode 100644 index 00000000000..651a0b12944 --- /dev/null +++ b/src/shared/Database/DBCfmt.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxx"; +const char AreaTriggerEntryfmt[]="niffffffff"; +const char BankBagSlotPricesEntryfmt[]="ni"; +const char BattlemasterListEntryfmt[]="niiixxxxxiiiixxssssssssssssssssxx"; +const char CharTitlesEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +const char ChatChannelsEntryfmt[]="iixssssssssssssssssxxxxxxxxxxxxxxxxxx"; + // ChatChannelsEntryfmt, index not used (more compact store) +const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; +const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char CreatureDisplayInfofmt[]="nxxxfxxxxxxxxx"; +const char CreatureFamilyfmt[]="nfifiiiissssssssssssssssxx"; +const char CreatureSpellDatafmt[]="nxxxxxxxx"; +const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; +const char DurabilityQualityfmt[]="nf"; +const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx"; +const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiissssssssssssssssxxxxxxxxxxxxxxxxxx"; +const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; +const char GemPropertiesEntryfmt[]="nixxi"; +const char GtCombatRatingsfmt[]="f"; +const char GtChanceToMeleeCritBasefmt[]="f"; +const char GtChanceToMeleeCritfmt[]="f"; +const char GtChanceToSpellCritBasefmt[]="f"; +const char GtChanceToSpellCritfmt[]="f"; +const char GtOCTRegenHPfmt[]="f"; +//const char GtOCTRegenMPfmt[]="f"; +const char GtRegenHPPerSptfmt[]="f"; +const char GtRegenMPPerSptfmt[]="f"; +const char Itemfmt[]="niii"; +//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; +//const char ItemCondExtCostsEntryfmt[]="xiii"; +const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiii"; +const char ItemRandomPropertiesfmt[]="nxiiixxxxxxxxxxxxxxxxxxx"; +const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiii"; +const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; +const char LockEntryfmt[]="niiiiixxxiiiiixxxiixxxxxxxxxxxxxx"; +const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char MapEntryfmt[]="nxixssssssssssssssssxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxiixxi"; +const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; +const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; +const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxi"; +const char SkillLineAbilityfmt[]="niiiixxiiiiixxi"; +const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellCastTimefmt[]="nixx"; +const char SpellDurationfmt[]="niii"; +const char SpellEntryfmt[]="nixiiiiiiiixiiiiixiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffixiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiixfffxxxiiii"; +const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; +const char SpellItemEnchantmentfmt[]="niiiiiixxxiiissssssssssssssssxiiii"; +const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; +const char SpellRadiusfmt[]="nfxf"; +const char SpellRangefmt[]="nffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx"; +const char StableSlotPricesfmt[] = "ni"; +const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxi"; +const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiix"; +const char TaxiNodesEntryfmt[]="nifffxxxxxxxxxxxxxxxxxii"; +const char TaxiPathEntryfmt[]="niii"; +const char TaxiPathNodeEntryfmt[]="diiifffiixx"; +const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +const char WorldMapAreaEntryfmt[]="xinxffffi"; +const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp new file mode 100644 index 00000000000..5726aeaa340 --- /dev/null +++ b/src/shared/Database/Database.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" +#include "Config/ConfigEnv.h" + +#include <ctime> +#include <iostream> +#include <fstream> + +Database::~Database() +{ + /*Delete objects*/ +} + +bool Database::Initialize(const char *) +{ + // Enable logging of SQL commands (usally only GM commands) + // (See method: PExecuteLog) + m_logSQL = sConfig.GetBoolDefault("LogSQL", false); + m_logsDir = sConfig.GetStringDefault("LogsDir",""); + if(!m_logsDir.empty()) + { + if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\')) + m_logsDir.append("/"); + } + + return true; +} + +void Database::ThreadStart() +{ +} + +void Database::ThreadEnd() +{ +} + +void Database::escape_string(std::string& str) +{ + if(str.empty()) + return; + + char* buf = new char[str.size()*2+1]; + escape_string(buf,str.c_str(),str.size()); + str = buf; + delete[] buf; +} + +bool Database::PExecuteLog(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + if( m_logSQL ) + { + time_t curr; + tm local; + time(&curr); // get current time_t value + local=*(localtime(&curr)); // dereference and assign + char fName[128]; + sprintf( fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday ); + + FILE* log_file; + std::string logsDir_fname = m_logsDir+fName; + log_file = fopen(logsDir_fname.c_str(), "a"); + if (log_file) + { + fprintf(log_file, "%s;\n", szQuery); + fclose(log_file); + } + else + { + // The file could not be opened + sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName); + } + } + + return Execute(szQuery); +} + +void Database::SetResultQueue(SqlResultQueue * queue) +{ + m_queryQueues[ZThread::ThreadImpl::current()] = queue; +} + +QueryResult* Database::PQuery(const char *format,...) +{ + if(!format) return NULL; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return Query(szQuery); +} + +bool Database::PExecute(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return Execute(szQuery); +} + +bool Database::DirectPExecute(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return DirectExecute(szQuery); +} diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h new file mode 100644 index 00000000000..40097773430 --- /dev/null +++ b/src/shared/Database/Database.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DATABASE_H +#define DATABASE_H + +#include "zthread/Thread.h" +#include "../src/zthread/ThreadImpl.h" +#include "Utilities/HashMap.h" +#include "Database/SqlDelayThread.h" + +class SqlTransaction; +class SqlResultQueue; +class SqlQueryHolder; + +typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlTransaction*> TransactionQueues; +typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlResultQueue*> QueryQueues; + +#define MAX_QUERY_LEN 1024 + +class MANGOS_DLL_SPEC Database +{ + protected: + Database() : m_threadBody(NULL), m_delayThread(NULL) {}; + + TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads + QueryQueues m_queryQueues; ///< Query queues from diff threads + SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer + ZThread::Thread* m_delayThread; ///< Pointer to executer thread + + public: + + virtual ~Database(); + + virtual bool Initialize(const char *infoString); + virtual void InitDelayThread() = 0; + virtual void HaltDelayThread() = 0; + + virtual QueryResult* Query(const char *sql) = 0; + QueryResult* PQuery(const char *format,...) ATTR_PRINTF(2,3); + + /// Async queries and query holders, implemented in DatabaseImpl.h + template<class Class> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql); + template<class Class, typename ParamType1> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql); + template<typename ParamType1> + bool AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql); + template<class Class> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) ATTR_PRINTF(4,5); + template<class Class, typename ParamType1> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(5,6); + template<typename ParamType1> + bool AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(5,6); + template<class Class> + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder); + template<class Class, typename ParamType1> + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1); + + virtual bool Execute(const char *sql) = 0; + bool PExecute(const char *format,...) ATTR_PRINTF(2,3); + virtual bool DirectExecute(const char* sql) = 0; + bool DirectPExecute(const char *format,...) ATTR_PRINTF(2,3); + + // Writes SQL commands to a LOG file (see mangosd.conf "LogSQL") + bool PExecuteLog(const char *format,...) ATTR_PRINTF(2,3); + + virtual bool BeginTransaction() // nothing do if DB not support transactions + { + return true; + } + virtual bool CommitTransaction() // nothing do if DB not support transactions + { + return true; + } + virtual bool RollbackTransaction() // can't rollback without transaction support + { + return false; + } + + virtual operator bool () const = 0; + + virtual unsigned long escape_string(char *to, const char *from, unsigned long length) { strncpy(to,from,length); return length; } + void escape_string(std::string& str); + + // must be called before first query in thread (one time for thread using one from existed Database objects) + virtual void ThreadStart(); + // must be called before finish thread run (one time for thread using one from existed Database objects) + virtual void ThreadEnd(); + + // sets the result queue of the current thread, be careful what thread you call this from + void SetResultQueue(SqlResultQueue * queue); + + private: + bool m_logSQL; + std::string m_logsDir; +}; +#endif diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h new file mode 100644 index 00000000000..e393b4d6de5 --- /dev/null +++ b/src/shared/Database/DatabaseEnv.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(DATABASEENV_H) +#define DATABASEENV_H + +#include "Common.h" +#include "Log.h" +#include "Errors.h" + +#include "Database/DBCStores.h" +#include "Database/Field.h" +#include "Database/QueryResult.h" + +#ifdef DO_POSTGRESQL +#include "Database/QueryResultPostgre.h" +#include "Database/Database.h" +#include "Database/DatabasePostgre.h" +typedef DatabasePostgre DatabaseType; +#define _LIKE_ "ILIKE" +#define _TABLE_SIM_ "\"" +#else +#include "Database/QueryResultMysql.h" +#include "Database/QueryResultSqlite.h" +#include "Database/Database.h" +#include "Database/DatabaseMysql.h" +#include "Database/DatabaseSqlite.h" +typedef DatabaseMysql DatabaseType; +#define _LIKE_ "LIKE" +#define _TABLE_SIM_ "`" +#endif + +extern DatabaseType WorldDatabase; +extern DatabaseType CharacterDatabase; +extern DatabaseType loginDatabase; + +#endif diff --git a/src/shared/Database/DatabaseImpl.h b/src/shared/Database/DatabaseImpl.h new file mode 100644 index 00000000000..682f7b25ef7 --- /dev/null +++ b/src/shared/Database/DatabaseImpl.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/Database.h" +#include "Database/SqlOperations.h" + +/// Function body definitions for the template function members of the Database class + +template<class Class> +bool +Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::QueryCallback<Class>(object, method), itr->second)); + return true; +} + +template<class Class, typename ParamType1> +bool +Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::QueryCallback<Class, ParamType1>(object, method, (QueryResult*)NULL, param1), itr->second)); + return true; +} + +template<typename ParamType1> +bool +Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::SQueryCallback<ParamType1>(method, (QueryResult*)NULL, param1), itr->second)); + return true; +} + +template<class Class> +bool +Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, szQuery); +} + +template<class Class, typename ParamType1> +bool +Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, param1, szQuery); +} + +template<typename ParamType1> +bool +Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(method, param1, szQuery); +} + + +template<class Class> +bool +Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder) +{ + if (!holder) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + holder->Execute(new MaNGOS::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult*)NULL, holder), m_threadBody, itr->second); + return true; +} + +template<class Class, typename ParamType1> +bool +Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1) +{ + if (!holder) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + holder->Execute(new MaNGOS::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult*)NULL, holder, param1), m_threadBody, itr->second); + return true; +} diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp new file mode 100644 index 00000000000..b736a60aa1b --- /dev/null +++ b/src/shared/Database/DatabaseMysql.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "Util.h" +#include "Policies/SingletonImp.h" +#include "Platform/Define.h" +#include "../src/zthread/ThreadImpl.h" +#include "DatabaseEnv.h" +#include "Database/MySQLDelayThread.h" +#include "Database/SqlOperations.h" +#include "Timer.h" + +void DatabaseMysql::ThreadStart() +{ + mysql_thread_init(); +} + +void DatabaseMysql::ThreadEnd() +{ + mysql_thread_end(); +} + +size_t DatabaseMysql::db_count = 0; + +DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) +{ + // before first connection + if( db_count++ == 0 ) + { + // Mysql Library Init + mysql_library_init(-1, NULL, NULL); + + if (!mysql_thread_safe()) + { + sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe."); + exit(1); + } + } +} + +DatabaseMysql::~DatabaseMysql() +{ + if (m_delayThread) + HaltDelayThread(); + + if (mMysql) + mysql_close(mMysql); + + //Free Mysql library pointers for last ~DB + if(--db_count == 0) + mysql_library_end(); +} + +bool DatabaseMysql::Initialize(const char *infoString) +{ + + if(!Database::Initialize(infoString)) + return false; + + tranThread = NULL; + MYSQL *mysqlInit; + mysqlInit = mysql_init(NULL); + if (!mysqlInit) + { + sLog.outError( "Could not initialize Mysql connection" ); + return false; + } + + InitDelayThread(); + + Tokens tokens = StrSplit(infoString, ";"); + + Tokens::iterator iter; + + std::string host, port_or_socket, user, password, database; + int port; + char const* unix_socket; + + iter = tokens.begin(); + + if(iter != tokens.end()) + host = *iter++; + if(iter != tokens.end()) + port_or_socket = *iter++; + if(iter != tokens.end()) + user = *iter++; + if(iter != tokens.end()) + password = *iter++; + if(iter != tokens.end()) + database = *iter++; + + mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8"); + #ifdef WIN32 + if(host==".") // named pipe use option (Windows) + { + unsigned int opt = MYSQL_PROTOCOL_PIPE; + mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt); + port = 0; + unix_socket = 0; + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #else + if(host==".") // socket use option (Unix/Linux) + { + unsigned int opt = MYSQL_PROTOCOL_SOCKET; + mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt); + host = "localhost"; + port = 0; + unix_socket = port_or_socket.c_str(); + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #endif + + mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(), + password.c_str(), database.c_str(), port, unix_socket, 0); + + if (mMysql) + { + sLog.outDetail( "Connected to MySQL database at %s", + host.c_str()); + sLog.outString( "MySQL client library: %s", mysql_get_client_info()); + sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql)); + + /*----------SET AUTOCOMMIT ON---------*/ + // It seems mysql 5.0.x have enabled this feature + // by default. In crash case you can lose data!!! + // So better to turn this off + // --- + // This is wrong since mangos use transactions, + // autocommit is turned of during it. + // Setting it to on makes atomic updates work + if (!mysql_autocommit(mMysql, 1)) + sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1"); + else + sLog.outDetail("AUTOCOMMIT NOT SET TO 1"); + /*-------------------------------------*/ + + // set connection properties to UTF8 to properly handle locales for different + // server configs - core sends data in UTF8, so MySQL must expect UTF8 too + PExecute("SET NAMES `utf8`"); + PExecute("SET CHARACTER SET `utf8`"); + + return true; + } + else + { + sLog.outError( "Could not connect to MySQL database at %s: %s\n", + host.c_str(),mysql_error(mysqlInit)); + mysql_close(mysqlInit); + return false; + } +} + +QueryResult* DatabaseMysql::Query(const char *sql) +{ + if (!mMysql) + return 0; + + MYSQL_RES *result = 0; + uint64 rowCount = 0; + uint32 fieldCount = 0; + + { + // guarded block for thread-safe mySQL request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + if(mysql_query(mMysql, sql)) + { + sLog.outErrorDb( "SQL: %s", sql ); + sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql)); + return NULL; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); + #endif + } + + result = mysql_store_result(mMysql); + + rowCount = mysql_affected_rows(mMysql); + fieldCount = mysql_field_count(mMysql); + // end guarded block + } + + if (!result ) + return NULL; + + if (!rowCount) + { + mysql_free_result(result); + return NULL; + } + + QueryResultMysql *queryResult = new QueryResultMysql(result, rowCount, fieldCount); + + queryResult->NextRow(); + + return queryResult; +} + +bool DatabaseMysql::Execute(const char *sql) +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) return DirectExecute(sql); + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { // Statement for transaction + i->second->DelayExecute(sql); + } + else + { + // Simple sql statement + m_threadBody->Delay(new SqlStatement(sql)); + } + + return true; +} + +bool DatabaseMysql::DirectExecute(const char* sql) +{ + if (!mMysql) + return false; + + { + // guarded block for thread-safe mySQL request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + if(mysql_query(mMysql, sql)) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql)); + return false; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); + #endif + } + // end guarded block + } + + return true; +} + +bool DatabaseMysql::_TransactionCmd(const char *sql) +{ + if (mysql_query(mMysql, sql)) + { + sLog.outError("SQL: %s", sql); + sLog.outError("SQL ERROR: %s", mysql_error(mMysql)); + return false; + } + else + { + DEBUG_LOG("SQL: %s", sql); + } + return true; +} + +bool DatabaseMysql::BeginTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread==ZThread::ThreadImpl::current()) + return false; // huh? this thread already started transaction + mMutex.acquire(); + if (!_TransactionCmd("START TRANSACTION")) + { + mMutex.release(); // can't start transaction + return false; + } + return true; // transaction started + } + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + // If for thread exists queue and also contains transaction + // delete that transaction (not allow trans in trans) + delete i->second; + + m_tranQueues[tranThread] = new SqlTransaction(); + + return true; +} + +bool DatabaseMysql::CommitTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("COMMIT"); + tranThread = NULL; + mMutex.release(); + return _res; + } + + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + m_threadBody->Delay(i->second); + i->second = NULL; + return true; + } + else + return false; +} + +bool DatabaseMysql::RollbackTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("ROLLBACK"); + tranThread = NULL; + mMutex.release(); + return _res; + } + + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + delete i->second; + i->second = NULL; + } + return true; +} + +unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length) +{ + if (!mMysql || !to || !from || !length) + return 0; + + return(mysql_real_escape_string(mMysql, to, from, length)); +} + +void DatabaseMysql::InitDelayThread() +{ + assert(!m_delayThread); + + //New delay thread for delay execute + m_delayThread = new ZThread::Thread(m_threadBody = new MySQLDelayThread(this)); +} + +void DatabaseMysql::HaltDelayThread() +{ + if (!m_threadBody || !m_delayThread) return; + + m_threadBody->Stop(); //Stop event + m_delayThread->wait(); //Wait for flush to DB + delete m_delayThread; //This also deletes m_threadBody + m_delayThread = NULL; + m_threadBody = NULL; +} +#endif diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h new file mode 100644 index 00000000000..2608212d52a --- /dev/null +++ b/src/shared/Database/DatabaseMysql.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#ifndef _DATABASEMYSQL_H +#define _DATABASEMYSQL_H + +#include "Database.h" +#include "Policies/Singleton.h" +#include "zthread/FastMutex.h" + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <mysql/mysql.h> +#else +#include <mysql.h> +#endif + +class MANGOS_DLL_SPEC DatabaseMysql : public Database +{ + friend class MaNGOS::OperatorNew<DatabaseMysql>; + + public: + DatabaseMysql(); + ~DatabaseMysql(); + + //! Initializes Mysql and connects to a server. + /*! infoString should be formated like hostname;username;password;database. */ + bool Initialize(const char *infoString); + void InitDelayThread(); + void HaltDelayThread(); + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + bool DirectExecute(const char* sql); + bool BeginTransaction(); + bool CommitTransaction(); + bool RollbackTransaction(); + + operator bool () const { return mMysql != NULL; } + + unsigned long escape_string(char *to, const char *from, unsigned long length); + using Database::escape_string; + + // must be call before first query in thread + void ThreadStart(); + // must be call before finish thread run + void ThreadEnd(); + private: + ZThread::FastMutex mMutex; + + ZThread::ThreadImpl* tranThread; + + MYSQL *mMysql; + + static size_t db_count; + + bool _TransactionCmd(const char *sql); +}; +#endif +#endif diff --git a/src/shared/Database/DatabasePostgre.cpp b/src/shared/Database/DatabasePostgre.cpp new file mode 100644 index 00000000000..637c2ac9522 --- /dev/null +++ b/src/shared/Database/DatabasePostgre.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_POSTGRESQL + +#include "Util.h" +#include "Policies/SingletonImp.h" +#include "Platform/Define.h" +#include "../src/zthread/ThreadImpl.h" +#include "DatabaseEnv.h" +#include "Database/PGSQLDelayThread.h" +#include "Database/SqlOperations.h" +#include "Timer.h" + +void DatabasePostgre::ThreadStart() +{ +} + +void DatabasePostgre::ThreadEnd() +{ +} + +size_t DatabasePostgre::db_count = 0; + +DatabasePostgre::DatabasePostgre() : Database(), mPGconn(NULL) +{ + // before first connection + if( db_count++ == 0 ) + { + + if (!PQisthreadsafe()) + { + sLog.outError("FATAL ERROR: PostgreSQL libpq isn't thread-safe."); + exit(1); + } + } +} + +DatabasePostgre::~DatabasePostgre() +{ + + if (m_delayThread) + HaltDelayThread(); + + if( mPGconn ) + { + PQfinish(mPGconn); + mPGconn = NULL; + } +} + +bool DatabasePostgre::Initialize(const char *infoString) +{ + if(!Database::Initialize(infoString)) + return false; + + tranThread = NULL; + + InitDelayThread(); + + Tokens tokens = StrSplit(infoString, ";"); + + Tokens::iterator iter; + + std::string host, port_or_socket, user, password, database; + + iter = tokens.begin(); + + if(iter != tokens.end()) + host = *iter++; + if(iter != tokens.end()) + port_or_socket = *iter++; + if(iter != tokens.end()) + user = *iter++; + if(iter != tokens.end()) + password = *iter++; + if(iter != tokens.end()) + database = *iter++; + + mPGconn = PQsetdbLogin(host.c_str(), port_or_socket.c_str(), NULL, NULL, database.c_str(), user.c_str(), password.c_str()); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(mPGconn) != CONNECTION_OK) + { + sLog.outError( "Could not connect to Postgre database at %s: %s", + host.c_str(), PQerrorMessage(mPGconn)); + PQfinish(mPGconn); + return false; + } + else + { + sLog.outDetail( "Connected to Postgre database at %s", + host.c_str()); + sLog.outString( "PostgreSQL server ver: %d",PQserverVersion(mPGconn)); + return true; + } + +} + +QueryResult* DatabasePostgre::Query(const char *sql) +{ + if (!mPGconn) + return 0; + + uint64 rowCount = 0; + uint32 fieldCount = 0; + + // guarded block for thread-safe request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + // Send the query + PGresult * result = PQexec(mPGconn, sql); + if (!result ) + { + return NULL; + } + + if (PQresultStatus(result) != PGRES_TUPLES_OK) + { + sLog.outErrorDb( "SQL : %s", sql ); + sLog.outErrorDb( "SQL %s", PQerrorMessage(mPGconn)); + PQclear(result); + return NULL; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTime() - _s, sql ); + #endif + } + + rowCount = PQntuples(result); + fieldCount = PQnfields(result); + // end guarded block + + if (!rowCount) + { + PQclear(result); + return NULL; + } + + QueryResultPostgre * queryResult = new QueryResultPostgre(result, rowCount, fieldCount); + queryResult->NextRow(); + + return queryResult; +} + +bool DatabasePostgre::Execute(const char *sql) +{ + + if (!mPGconn) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) return DirectExecute(sql); + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { // Statement for transaction + i->second->DelayExecute(sql); + } + else + { + // Simple sql statement + m_threadBody->Delay(new SqlStatement(sql)); + } + + return true; +} + +bool DatabasePostgre::DirectExecute(const char* sql) +{ + if (!mPGconn) + return false; + { + // guarded block for thread-safe request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + PGresult *res = PQexec(mPGconn, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + sLog.outErrorDb( "SQL: %s", sql ); + sLog.outErrorDb( "SQL %s", PQerrorMessage(mPGconn) ); + return false; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTime() - _s, sql ); + #endif + } + PQclear(res); + + // end guarded block + } + return true; +} + +bool DatabasePostgre::_TransactionCmd(const char *sql) +{ + if (!mPGconn) + return false; + + PGresult *res = PQexec(mPGconn, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + sLog.outError("SQL: %s", sql); + sLog.outError("SQL ERROR: %s", PQerrorMessage(mPGconn)); + return false; + } + else + { + DEBUG_LOG("SQL: %s", sql); + } + return true; +} + +bool DatabasePostgre::BeginTransaction() +{ + if (!mPGconn) + return false; + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread==ZThread::ThreadImpl::current()) + return false; // huh? this thread already started transaction + mMutex.acquire(); + if (!_TransactionCmd("START TRANSACTION")) + { + mMutex.release(); // can't start transaction + return false; + } + return true; + } + // transaction started + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + // If for thread exists queue and also contains transaction + // delete that transaction (not allow trans in trans) + delete i->second; + + m_tranQueues[tranThread] = new SqlTransaction(); + + return true; +} + +bool DatabasePostgre::CommitTransaction() +{ + if (!mPGconn) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("COMMIT"); + tranThread = NULL; + mMutex.release(); + return _res; + } + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + m_threadBody->Delay(i->second); + i->second = NULL; + return true; + } + else + return false; +} + +bool DatabasePostgre::RollbackTransaction() +{ + if (!mPGconn) + return false; + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("ROLLBACK"); + tranThread = NULL; + mMutex.release(); + return _res; + } + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + delete i->second; + i->second = NULL; + } + return true; +} + +unsigned long DatabasePostgre::escape_string(char *to, const char *from, unsigned long length) +{ + if (!mPGconn || !to || !from || !length) + return 0; + + return PQescapeString(to, from, length); +} + +void DatabasePostgre::InitDelayThread() +{ + assert(!m_delayThread); + + //New delay thread for delay execute + m_delayThread = new ZThread::Thread(m_threadBody = new PGSQLDelayThread(this)); +} + +void DatabasePostgre::HaltDelayThread() +{ + if (!m_threadBody || !m_delayThread) return; + + m_threadBody->Stop(); //Stop event + m_delayThread->wait(); //Wait for flush to DB + delete m_delayThread; //This also deletes m_threadBody + m_delayThread = NULL; + m_threadBody = NULL; +} +#endif diff --git a/src/shared/Database/DatabasePostgre.h b/src/shared/Database/DatabasePostgre.h new file mode 100644 index 00000000000..b1929c46360 --- /dev/null +++ b/src/shared/Database/DatabasePostgre.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DatabasePostgre_H +#define _DatabasePostgre_H + +#include "Policies/Singleton.h" +#include "zthread/FastMutex.h" +#include <stdarg.h> + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <postgre/libpq-fe.h> +#else +#include <libpq-fe.h> +#endif + +class DatabasePostgre : public Database +{ + friend class MaNGOS::OperatorNew<DatabasePostgre>; + + public: + DatabasePostgre(); + ~DatabasePostgre(); + + //! Initializes Postgres and connects to a server. + /*! infoString should be formated like hostname;username;password;database. */ + bool Initialize(const char *infoString); + void InitDelayThread(); + void HaltDelayThread(); + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + bool DirectExecute(const char* sql); + bool BeginTransaction(); + bool CommitTransaction(); + bool RollbackTransaction(); + + operator bool () const { return mPGconn != NULL; } + + unsigned long escape_string(char *to, const char *from, unsigned long length); + using Database::escape_string; + + // must be call before first query in thread + void ThreadStart(); + // must be call before finish thread run + void ThreadEnd(); + private: + ZThread::FastMutex mMutex; + ZThread::FastMutex tranMutex; + + ZThread::ThreadImpl* tranThread; + + PGconn *mPGconn; + + static size_t db_count; + + bool _TransactionCmd(const char *sql); +}; +#endif diff --git a/src/shared/Database/DatabaseSqlite.cpp b/src/shared/Database/DatabaseSqlite.cpp new file mode 100644 index 00000000000..75307f9d430 --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +DatabaseSqlite::DatabaseSqlite() : Database(), mSqlite(0) +{ +} + +DatabaseSqlite::~DatabaseSqlite() +{ + if (mSqlite) + sqlite_close(mSqlite); +} + +bool DatabaseSqlite::Initialize(const char *infoString) +{ + if(!Database::Initialize(infoString)) + return false; + + char *errmsg; + + mSqlite = sqlite_open(infoString, 0, &errmsg); + + if (!mSqlite) + { + + if (errmsg) + sqlite_freemem(errmsg); + return false; + } + + return true; +} + +QueryResult* DatabaseSqlite::Query(const char *sql) +{ + char *errmsg; + + if (!mSqlite) + return 0; + + char **tableData; + int rowCount; + int fieldCount; + + sqlite_get_table(mSqlite, sql, &tableData, &rowCount, &fieldCount, &errmsg); + + if (!rowCount) + return 0; + + if (!tableData) + { + + if (errmsg) + sqlite_freemem(errmsg); + return 0; + } + + QueryResultSqlite *queryResult = new QueryResultSqlite(tableData, rowCount, fieldCount); + if(!queryResult) + { + + return 0; + } + + queryResult->NextRow(); + + return queryResult; +} + +bool DatabaseSqlite::Execute(const char *sql) +{ + char *errmsg; + + if (!mSqlite) + return false; + + if(sqlite_exec(mSqlite, sql, NULL, NULL, &errmsg) != SQLITE_OK) + return false; + + return true; +} +#endif diff --git a/src/shared/Database/DatabaseSqlite.h b/src/shared/Database/DatabaseSqlite.h new file mode 100644 index 00000000000..37190a6b562 --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#ifndef _DATABASESQLITE_H +#define _DATABASESQLITE_H + +#include <sqlite/sqlite.h> + +class DatabaseSqlite : public Database +{ + public: + DatabaseSqlite(); + ~DatabaseSqlite(); + + bool Initialize(const char *infoString); + + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + + operator bool () const { return mSqlite != NULL; } + + private: + sqlite *mSqlite; +}; +#endif +#endif diff --git a/src/shared/Database/Field.cpp b/src/shared/Database/Field.cpp new file mode 100644 index 00000000000..5c693403c06 --- /dev/null +++ b/src/shared/Database/Field.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" + +Field::Field() : +mValue(NULL), mType(DB_TYPE_UNKNOWN) +{ +} + +Field::Field(Field &f) +{ + const char *value; + + value = f.GetString(); + + if (value && (mValue = new char[strlen(value) + 1])) + strcpy(mValue, value); + else + mValue = NULL; + + mType = f.GetType(); +} + +Field::Field(const char *value, enum Field::DataTypes type) : +mType(type) +{ + if (value && (mValue = new char[strlen(value) + 1])) + strcpy(mValue, value); + else + mValue = NULL; +} + +Field::~Field() +{ + if(mValue) delete [] mValue; +} + +void Field::SetValue(const char *value) +{ + if(mValue) delete [] mValue; + + if (value) + { + mValue = new char[strlen(value) + 1]; + strcpy(mValue, value); + } + else + mValue = NULL; +} diff --git a/src/shared/Database/Field.h b/src/shared/Database/Field.h new file mode 100644 index 00000000000..f829ec18424 --- /dev/null +++ b/src/shared/Database/Field.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(FIELD_H) +#define FIELD_H + +class Field +{ + public: + + enum DataTypes + { + DB_TYPE_UNKNOWN = 0x00, + DB_TYPE_STRING = 0x01, + DB_TYPE_INTEGER = 0x02, + DB_TYPE_FLOAT = 0x03, + DB_TYPE_BOOL = 0x04 + }; + + Field(); + Field(Field &f); + Field(const char *value, enum DataTypes type); + + ~Field(); + + enum DataTypes GetType() const { return mType; } + + const char *GetString() const { return mValue; } + std::string GetCppString() const + { + return mValue ? mValue : ""; // std::string s = 0 have undefine result in C++ + } + float GetFloat() const { return mValue ? static_cast<float>(atof(mValue)) : 0.0f; } + bool GetBool() const { return mValue ? atoi(mValue) > 0 : false; } + int32 GetInt32() const { return mValue ? static_cast<int32>(atol(mValue)) : int32(0); } + uint8 GetUInt8() const { return mValue ? static_cast<uint8>(atol(mValue)) : uint8(0); } + uint16 GetUInt16() const { return mValue ? static_cast<uint16>(atol(mValue)) : uint16(0); } + int16 GetInt16() const { return mValue ? static_cast<int16>(atol(mValue)) : int16(0); } + uint32 GetUInt32() const { return mValue ? static_cast<uint32>(atol(mValue)) : uint32(0); } + uint64 GetUInt64() const + { + if(mValue) + { + uint64 value; + sscanf(mValue,I64FMTD,&value); + return value; + } + else + return 0; + } + + void SetType(enum DataTypes type) { mType = type; } + + void SetValue(const char *value); + + private: + char *mValue; + enum DataTypes mType; +}; +#endif diff --git a/src/shared/Database/Makefile.am b/src/shared/Database/Makefile.am new file mode 100644 index 00000000000..2d3a9fb5d5c --- /dev/null +++ b/src/shared/Database/Makefile.am @@ -0,0 +1,62 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosdatabase.a + +libmangosdatabase_a_SOURCES = \ + DBCStores.cpp \ + DBCStores.h \ + DBCStructure.h \ + DBCfmt.cpp \ + Database.cpp \ + Database.h \ + DatabaseEnv.h \ + DatabaseImpl.h \ + DatabaseMysql.cpp \ + DatabasePostgre.cpp \ + DatabaseMysql.h \ + DatabasePostgre.h \ + DatabaseSqlite.cpp \ + DatabaseSqlite.h \ + Field.cpp \ + Field.h \ + MySQLDelayThread.h \ + PGSQLDelayThread.h \ + QueryResult.h \ + QueryResultMysql.cpp \ + QueryResultMysql.h \ + QueryResultPostgre.cpp \ + QueryResultPostgre.h \ + QueryResultSqlite.cpp \ + QueryResultSqlite.h \ + SQLStorage.cpp \ + SQLStorage.h \ + SqlDelayThread.cpp \ + SqlDelayThread.h \ + SqlOperations.cpp \ + SqlOperations.h \ + dbcfile.cpp \ + dbcfile.h diff --git a/src/shared/Database/MySQLDelayThread.h b/src/shared/Database/MySQLDelayThread.h new file mode 100644 index 00000000000..dcc979d6d53 --- /dev/null +++ b/src/shared/Database/MySQLDelayThread.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MYSQLDELAYTHREAD_H +#define __MYSQLDELAYTHREAD_H + +#include "Database/SqlDelayThread.h" + +class MySQLDelayThread : public SqlDelayThread +{ + public: + MySQLDelayThread(Database* db) : SqlDelayThread(db) {} + void Stop() { SqlDelayThread::Stop(); } +}; +#endif //__MYSQLDELAYTHREAD_H diff --git a/src/shared/Database/PGSQLDelayThread.h b/src/shared/Database/PGSQLDelayThread.h new file mode 100644 index 00000000000..03907026899 --- /dev/null +++ b/src/shared/Database/PGSQLDelayThread.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGSQLDELAYTHREAD_H +#define __PGSQLDELAYTHREAD_H + +#include "Database/SqlDelayThread.h" + +class PGSQLDelayThread : public SqlDelayThread +{ + public: + PGSQLDelayThread(Database* db) : SqlDelayThread(db) {} + void Stop() { SqlDelayThread::Stop(); } +}; +#endif //__PGSQLDELAYTHREAD_H diff --git a/src/shared/Database/QueryResult.h b/src/shared/Database/QueryResult.h new file mode 100644 index 00000000000..50ee98a9a22 --- /dev/null +++ b/src/shared/Database/QueryResult.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(QUERYRESULT_H) +#define QUERYRESULT_H + +class MANGOS_DLL_SPEC QueryResult +{ + public: + QueryResult(uint64 rowCount, uint32 fieldCount) + : mFieldCount(fieldCount), mRowCount(rowCount) {} + + virtual ~QueryResult() {} + + virtual bool NextRow() = 0; + + typedef std::map<uint32, std::string> FieldNames; + + uint32 GetField_idx(const std::string &name) const + { + for(FieldNames::const_iterator iter = GetFiedNames().begin(); iter != GetFiedNames().end(); ++iter) + { + if(iter->second == name) + return iter->first; + } + assert(false && "unknown field name"); + return uint32(-1); + } + + Field *Fetch() const { return mCurrentRow; } + + const Field & operator [] (int index) const { return mCurrentRow[index]; } + + const Field & operator [] (const std::string &name) const + { + return mCurrentRow[GetField_idx(name)]; + } + + uint32 GetFieldCount() const { return mFieldCount; } + uint64 GetRowCount() const { return mRowCount; } + FieldNames const& GetFiedNames() const {return mFieldNames; } + + protected: + Field *mCurrentRow; + uint32 mFieldCount; + uint64 mRowCount; + FieldNames mFieldNames; +}; +#endif diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp new file mode 100644 index 00000000000..b24e51c2352 --- /dev/null +++ b/src/shared/Database/QueryResultMysql.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultMysql::QueryResultMysql(MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mResult(result) +{ + + mCurrentRow = new Field[mFieldCount]; + ASSERT(mCurrentRow); + + MYSQL_FIELD *fields = mysql_fetch_fields(mResult); + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = fields[i].name; + mCurrentRow[i].SetType(ConvertNativeType(fields[i].type)); + } +} + +QueryResultMysql::~QueryResultMysql() +{ + EndQuery(); +} + +bool QueryResultMysql::NextRow() +{ + MYSQL_ROW row; + + if (!mResult) + return false; + + row = mysql_fetch_row(mResult); + if (!row) + { + EndQuery(); + return false; + } + + for (uint32 i = 0; i < mFieldCount; i++) + mCurrentRow[i].SetValue(row[i]); + + return true; +} + +void QueryResultMysql::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = 0; + } + + if (mResult) + { + mysql_free_result(mResult); + mResult = 0; + } +} + +enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysqlType) const +{ + switch (mysqlType) + { + case FIELD_TYPE_TIMESTAMP: + case FIELD_TYPE_DATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_BLOB: + case FIELD_TYPE_SET: + case FIELD_TYPE_NULL: + return Field::DB_TYPE_STRING; + case FIELD_TYPE_TINY: + + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONGLONG: + case FIELD_TYPE_ENUM: + return Field::DB_TYPE_INTEGER; + case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + return Field::DB_TYPE_FLOAT; + default: + return Field::DB_TYPE_UNKNOWN; + } +} +#endif diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h new file mode 100644 index 00000000000..e62a1a4c120 --- /dev/null +++ b/src/shared/Database/QueryResultMysql.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#if !defined(QUERYRESULTMYSQL_H) +#define QUERYRESULTMYSQL_H + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <mysql/mysql.h> +#else +#include <mysql.h> +#endif + +class QueryResultMysql : public QueryResult +{ + public: + QueryResultMysql(MYSQL_RES *result, uint64 rowCount, uint32 fieldCount); + + ~QueryResultMysql(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const; + void EndQuery(); + + MYSQL_RES *mResult; +}; +#endif +#endif diff --git a/src/shared/Database/QueryResultPostgre.cpp b/src/shared/Database/QueryResultPostgre.cpp new file mode 100644 index 00000000000..2cb6447b170 --- /dev/null +++ b/src/shared/Database/QueryResultPostgre.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultPostgre::QueryResultPostgre(PGresult *result, uint64 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mResult(result), mTableIndex(0) +{ + + mCurrentRow = new Field[mFieldCount]; + ASSERT(mCurrentRow); + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = PQfname(result, i); + mCurrentRow[i].SetType(ConvertNativeType(PQftype( result, i ))); + } +} + +QueryResultPostgre::~QueryResultPostgre() +{ + EndQuery(); +} + +bool QueryResultPostgre::NextRow() +{ + if (!mResult) + return false; + + if (mTableIndex >= mRowCount) + { + EndQuery(); + return false; + } + + char* pPQgetvalue; + for (int j = 0; j < mFieldCount; j++) + { + pPQgetvalue = PQgetvalue(mResult, mTableIndex, j); + if(pPQgetvalue && !(*pPQgetvalue)) + pPQgetvalue = NULL; + + mCurrentRow[j].SetValue(pPQgetvalue); + } + ++mTableIndex; + + return true; +} + +void QueryResultPostgre::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = 0; + } + + if (mResult) + { + PQclear(mResult); + mResult = 0; + } +} + +// see types in #include <postgre/pg_type.h> +enum Field::DataTypes QueryResultPostgre::ConvertNativeType(Oid pOid ) const +{ + switch (pOid) + { + case BPCHAROID: + case CIDOID: + case CIDROID: + case CIRCLEOID: + case INETOID: + case NAMEOID: + case TEXTOID: + case VARCHAROID: + return Field::DB_TYPE_STRING; + case CASHOID: + case FLOAT4OID: + case FLOAT8OID: + case NUMERICOID: + return Field::DB_TYPE_FLOAT; + case DATEOID: // Date + case RELTIMEOID: // Date + case TIMEOID: // Time + case TIMETZOID: // Time + case ABSTIMEOID: // DateTime + case INTERVALOID: // DateTime + case TIMESTAMPOID: // DateTime + case TIMESTAMPTZOID: // DateTime + case INT2OID: // Int + case INT2VECTOROID: // Int + case INT4OID: // Int + case OIDOID: // Int + case CHAROID: // UInt + case INT8OID: // LongLong + return Field::DB_TYPE_INTEGER; + case BOOLOID: + return Field::DB_TYPE_BOOL; // Bool +/* + case BOXOID: Rect; + case LINEOID: Rect; + case VARBITOID: BitArray; + case BYTEAOID: ByteArray; +*/ + case LSEGOID: + case OIDVECTOROID: + case PATHOID: + case POINTOID: + case POLYGONOID: + case REGPROCOID: + case TIDOID: + case TINTERVALOID: + case UNKNOWNOID: + case XIDOID: + default: + return Field::DB_TYPE_UNKNOWN; + } + return Field::DB_TYPE_UNKNOWN; +} +#endif diff --git a/src/shared/Database/QueryResultPostgre.h b/src/shared/Database/QueryResultPostgre.h new file mode 100644 index 00000000000..f6726d20123 --- /dev/null +++ b/src/shared/Database/QueryResultPostgre.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(QUERYRESULTPOSTGRE_H) +#define QUERYRESULTPOSTGRE_H + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <postgre/libpq-fe.h> +#include <postgre/pg_type.h> +#else +#include <libpq-fe.h> +//#include <pg_type.h> +#endif + +class QueryResultPostgre : public QueryResult +{ + public: + QueryResultPostgre(PGresult *result, uint64 rowCount, uint32 fieldCount); + + ~QueryResultPostgre(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(Oid pOid) const; + void EndQuery(); + + PGresult *mResult; + uint32 mTableIndex; +}; +#endif diff --git a/src/shared/Database/QueryResultSqlite.cpp b/src/shared/Database/QueryResultSqlite.cpp new file mode 100644 index 00000000000..3eca2f08d46 --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultSqlite::QueryResultSqlite(char **tableData, uint32 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mTableData(tableData), mTableIndex(0) +{ + mCurrentRow = new Field[mFieldCount]; + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = mTableData[i]; + mCurrentRow[i].SetType(Field::DB_TYPE_UNKNOWN); + } +} + +QueryResultSqlite::~QueryResultSqlite() +{ + EndQuery(); +} + +bool QueryResultSqlite::NextRow() +{ + int startIndex; + uint32 i; + + if (!mTableData) + return false; + + if (mTableIndex >= mRowCount) + { + EndQuery(); + return false; + } + + startIndex = (mTableIndex + 1) * mFieldCount; + for (i = 0; i < mFieldCount; i++) + { + mCurrentRow[i].SetValue(mTableData[startIndex + i]); + } + + ++mTableIndex; + return true; +} + +void QueryResultSqlite::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = NULL; + } + if (mTableData) + { + sqlite_free_table(mTableData); + mTableData = NULL; + } +} + +enum Field::DataTypes QueryResultSqlite::ConvertNativeType(const char* sqliteType) const +{ + if (sqliteType) + { + switch (*sqliteType) + { + case 'S': + return Field::DB_TYPE_STRING; + case 'I': + return Field::DB_TYPE_INTEGER; + case 'F': + return Field::DB_TYPE_FLOAT; + default: + return Field::DB_TYPE_UNKNOWN; + }; + } + return Field::DB_TYPE_UNKNOWN; +} +#endif diff --git a/src/shared/Database/QueryResultSqlite.h b/src/shared/Database/QueryResultSqlite.h new file mode 100644 index 00000000000..c72e5727999 --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#if !defined(QUERYRESULTSQLITE_H) +#define QUERYRESULTSQLITE_H + +#include <sqlite/sqlite.h> + +class QueryResultSqlite : public QueryResult +{ + public: + QueryResultSqlite(char **tableData, uint32 rowCount, uint32 fieldCount); + + ~QueryResultSqlite(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(const char* sqliteType) const; + void EndQuery(); + + char **mTableData; + uint32 mTableIndex; +}; +#endif +#endif diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp new file mode 100644 index 00000000000..6041eaf282e --- /dev/null +++ b/src/shared/Database/SQLStorage.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SQLStorage.h" +#include "ProgressBar.h" +#include "Log.h" +#include "dbcfile.h" + +#ifdef DO_POSTGRESQL +extern DatabasePostgre WorldDatabase; +#else +extern DatabaseMysql WorldDatabase; +#endif + +const char CreatureInfofmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiilliiis"; +const char CreatureDataAddonInfofmt[]="iiiiiiis"; +const char CreatureModelfmt[]="iffbi"; +const char CreatureInfoAddonInfofmt[]="iiiiiiis"; +const char EquipmentInfofmt[]="iiiiiiiiii"; +const char GameObjectInfofmt[]="iiissiifiiiiiiiiiiiiiiiiiiiiiiiis"; +const char ItemPrototypefmt[]="iiiisiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffiffiffiffiffiiiiiiiiiifiiifiiiiiifiiiiiifiiiiiifiiiiiifiiiisiiiiiiiiiiiiiiiiiiiiiiiiifsiiiii"; +const char PageTextfmt[]="isi"; +const char SpellThreatfmt[]="ii"; +const char InstanceTemplatefmt[]="iiiiiiffffs"; + +SQLStorage sCreatureStorage(CreatureInfofmt,"entry","creature_template"); +SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon"); +SQLStorage sCreatureModelStorage(CreatureModelfmt,"modelid","creature_model_info"); +SQLStorage sCreatureInfoAddonStorage(CreatureInfoAddonInfofmt,"entry","creature_template_addon"); +SQLStorage sEquipmentStorage(EquipmentInfofmt,"entry","creature_equip_template"); +SQLStorage sGOStorage(GameObjectInfofmt,"entry","gameobject_template"); +SQLStorage sItemStorage(ItemPrototypefmt,"entry","item_template"); +SQLStorage sPageTextStore(PageTextfmt,"entry","page_text"); +SQLStorage sSpellThreatStore(SpellThreatfmt,"entry","spell_threat"); +SQLStorage sInstanceTemplate(InstanceTemplatefmt,"map","instance_template"); + +void SQLStorage::Free () +{ + uint32 offset=0; + for(uint32 x=0;x<iNumFields;x++) + if (format[x]==FT_STRING) + { + for(uint32 y=0;y<MaxEntry;y++) + if(pIndex[y]) + delete [] *(char**)((char*)(pIndex[y])+offset); + + offset+=sizeof(char*); + } + else if (format[x]==FT_LOGIC) + offset+=sizeof(bool); + else if (format[x]==FT_BYTE) + offset+=sizeof(char); + else + offset+=4; + + delete [] pIndex; + delete [] data; +} + +void SQLStorage::Load () +{ + uint32 maxi; + Field *fields; + QueryResult *result = WorldDatabase.PQuery("SELECT MAX(%s) FROM %s",entry_field,table); + if(!result) + { + sLog.outError("Error loading %s table (not exist?)\n",table); + exit(1); // Stop server at loading non exited table or not accessable table + } + + maxi= (*result)[0].GetUInt32()+1; + delete result; + + result = WorldDatabase.PQuery("SELECT COUNT(*) FROM %s",table); + if(result) + { + fields = result->Fetch(); + RecordCount=fields[0].GetUInt32(); + delete result; + } + else + RecordCount = 0; + + result = WorldDatabase.PQuery("SELECT * FROM %s",table); + + if(!result) + { + sLog.outError("%s table is empty!\n",table); + RecordCount = 0; + return; + } + + uint32 recordsize=0; + uint32 offset=0; + + if(iNumFields!=result->GetFieldCount()) + { + RecordCount = 0; + sLog.outError("Error in %s table, probably sql file format was updated (there should be %d fields in sql).\n",table,iNumFields); + delete result; + exit(1); // Stop server at loading broken or non-compatiable table. + } + + //get struct size + uint32 sc=0; + uint32 bo=0; + uint32 bb=0; + for(uint32 x=0;x<iNumFields;x++) + if(format[x]==FT_STRING) + ++sc; + else if (format[x]==FT_LOGIC) + ++bo; + else if (format[x]==FT_BYTE) + ++bb; + recordsize=(iNumFields-sc-bo-bb)*4+sc*sizeof(char*)+bo*sizeof(bool)+bb*sizeof(char); + + char** newIndex=new char*[maxi]; + memset(newIndex,0,maxi*sizeof(char*)); + + char * _data= new char[RecordCount *recordsize]; + uint32 count=0; + barGoLink bar( RecordCount ); + do + { + fields = result->Fetch(); + bar.step(); + char *p=(char*)&_data[recordsize*count]; + newIndex[fields[0].GetUInt32()]=p; + + offset=0; + for(uint32 x=0;x<iNumFields;x++) + switch(format[x]) + { + case FT_LOGIC: + *((bool*)(&p[offset]))=(fields[x].GetUInt32()>0); + offset+=sizeof(bool); + break; + case FT_BYTE: + *((char*)(&p[offset]))=(fields[x].GetUInt8()); + offset+=sizeof(char); + break; + case FT_INT: + *((uint32*)(&p[offset]))=fields[x].GetUInt32(); + offset+=sizeof(uint32); + break; + case FT_FLOAT: + *((float*)(&p[offset]))=fields[x].GetFloat(); + offset+=sizeof(float); + break; + case FT_STRING: + char const* tmp = fields[x].GetString(); + char* st; + if(!tmp) + { + st=new char[1]; + *st=0; + } + else + { + uint32 l=strlen(tmp)+1; + st=new char[l]; + memcpy(st,tmp,l); + } + *((char**)(&p[offset]))=st; + offset+=sizeof(char*); + break; + } + ++count; + }while( result->NextRow() ); + + delete result; + + pIndex =newIndex; + MaxEntry=maxi; + data=_data; +} diff --git a/src/shared/Database/SQLStorage.h b/src/shared/Database/SQLStorage.h new file mode 100644 index 00000000000..4ddd44f593e --- /dev/null +++ b/src/shared/Database/SQLStorage.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SQLSTORAGE_H +#define SQLSTORAGE_H + +#include "Common.h" +#include "Database/DatabaseEnv.h" + +class SQLStorage +{ + public: + + SQLStorage(const char*fmt,const char * _entry_field,const char * sqlname) + { + format=fmt; + entry_field = _entry_field; + table=sqlname; + data=NULL; + pIndex=NULL; + iNumFields =strlen(fmt); + MaxEntry = 0; + } + ~SQLStorage() + { + Free(); + } + + template<class T> + T const* LookupEntry(uint32 id) const + { + if( id == 0 ) + return NULL; + if(id >= MaxEntry) + return NULL; + return reinterpret_cast<T const*>(pIndex[id]); + } + + uint32 RecordCount; + uint32 MaxEntry; + uint32 iNumFields; + void Load(); + void Free(); + private: + char** pIndex; + + char *data; + const char *format; + const char *table; + const char *entry_field; + //bool HasString; +}; +#endif diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp new file mode 100644 index 00000000000..257a3d686e8 --- /dev/null +++ b/src/shared/Database/SqlDelayThread.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/SqlDelayThread.h" +#include "Database/SqlOperations.h" +#include "DatabaseEnv.h" + +SqlDelayThread::SqlDelayThread(Database* db) : m_dbEngine(db), m_running(true) +{ +} + +void SqlDelayThread::run() +{ + SqlOperation* s; + #ifndef DO_POSTGRESQL + mysql_thread_init(); + #endif + + while (m_running) + { + // if the running state gets turned off while sleeping + // empty the queue before exiting + ZThread::Thread::sleep(10); + while (!m_sqlQueue.empty()) + { + s = m_sqlQueue.next(); + s->Execute(m_dbEngine); + delete s; + } + } + + #ifndef DO_POSTGRESQL + mysql_thread_end(); + #endif +} + +void SqlDelayThread::Stop() +{ + m_running = false; +} diff --git a/src/shared/Database/SqlDelayThread.h b/src/shared/Database/SqlDelayThread.h new file mode 100644 index 00000000000..dd729660dba --- /dev/null +++ b/src/shared/Database/SqlDelayThread.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SQLDELAYTHREAD_H +#define __SQLDELAYTHREAD_H + +#include "zthread/Thread.h" +#include "zthread/Runnable.h" +#include "zthread/FastMutex.h" +#include "zthread/LockedQueue.h" + +class Database; +class SqlOperation; + +class SqlDelayThread : public ZThread::Runnable +{ + typedef ZThread::LockedQueue<SqlOperation*, ZThread::FastMutex> SqlQueue; + private: + SqlQueue m_sqlQueue; ///< Queue of SQL statements + Database* m_dbEngine; ///< Pointer to used Database engine + bool m_running; + + SqlDelayThread(); + public: + SqlDelayThread(Database* db); + + ///< Put sql statement to delay queue + inline void Delay(SqlOperation* sql) { m_sqlQueue.add(sql); } + + virtual void Stop(); ///< Stop event + virtual void run(); ///< Main Thread loop +}; +#endif //__SQLDELAYTHREAD_H diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp new file mode 100644 index 00000000000..38febadbc5c --- /dev/null +++ b/src/shared/Database/SqlOperations.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SqlOperations.h" +#include "SqlDelayThread.h" +#include "DatabaseEnv.h" +#include "DatabaseImpl.h" + +/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + +void SqlStatement::Execute(Database *db) +{ + /// just do it + db->DirectExecute(m_sql); +} + +void SqlTransaction::Execute(Database *db) +{ + if(m_queue.empty()) + return; + db->DirectExecute("START TRANSACTION"); + while(!m_queue.empty()) + { + char const *sql = m_queue.front(); + m_queue.pop(); + + if(!db->DirectExecute(sql)) + { + free((void*)const_cast<char*>(sql)); + db->DirectExecute("ROLLBACK"); + while(!m_queue.empty()) + { + free((void*)const_cast<char*>(m_queue.front())); + m_queue.pop(); + } + return; + } + + free((void*)const_cast<char*>(sql)); + } + db->DirectExecute("COMMIT"); +} + +/// ---- ASYNC QUERIES ---- + +void SqlQuery::Execute(Database *db) +{ + if(!m_callback || !m_queue) + return; + /// execute the query and store the result in the callback + m_callback->SetResult(db->Query(m_sql)); + /// add the callback to the sql result queue of the thread it originated from + m_queue->add(m_callback); +} + +void SqlResultQueue::Update() +{ + /// execute the callbacks waiting in the synchronization queue + while(!empty()) + { + MaNGOS::IQueryCallback * callback = next(); + callback->Execute(); + delete callback; + } +} + +void SqlQueryHolder::Execute(MaNGOS::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue) +{ + if(!callback || !thread || !queue) + return; + + /// delay the execution of the queries, sync them with the delay thread + /// which will in turn resync on execution (via the queue) and call back + SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue); + thread->Delay(holderEx); +} + +bool SqlQueryHolder::SetQuery(size_t index, const char *sql) +{ + if(m_queries.size() <= index) + { + sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,m_queries.size(),sql); + return false; + } + + if(m_queries[index].first != NULL) + { + sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])",index,m_queries.size(),m_queries[index].first,sql); + return false; + } + + /// not executed yet, just stored (it's not called a holder for nothing) + m_queries[index] = SqlResultPair(strdup(sql), NULL); + return true; +} + +bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) +{ + if(!format) + { + sLog.outError("Query (index: %u) is empty.",index); + return false; + } + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return SetQuery(index,szQuery); +} + +QueryResult* SqlQueryHolder::GetResult(size_t index) +{ + if(index < m_queries.size()) + { + /// the query strings are freed on the first GetResult or in the destructor + if(m_queries[index].first != NULL) + { + free((void*)(const_cast<char*>(m_queries[index].first))); + m_queries[index].first = NULL; + } + /// when you get a result aways remember to delete it! + return m_queries[index].second; + } + else + return NULL; +} + +void SqlQueryHolder::SetResult(size_t index, QueryResult *result) +{ + /// store the result in the holder + if(index < m_queries.size()) + m_queries[index].second = result; +} + +SqlQueryHolder::~SqlQueryHolder() +{ + for(size_t i = 0; i < m_queries.size(); i++) + { + /// if the result was never used, free the resources + /// results used already (getresult called) are expected to be deleted + if(m_queries[i].first != NULL) + { + free((void*)(const_cast<char*>(m_queries[i].first))); + if(m_queries[i].second) + delete m_queries[i].second; + } + } +} + +void SqlQueryHolder::SetSize(size_t size) +{ + /// to optimize push_back, reserve the number of queries about to be executed + m_queries.resize(size); +} + +void SqlQueryHolderEx::Execute(Database *db) +{ + if(!m_holder || !m_callback || !m_queue) + return; + + /// we can do this, we are friends + std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries; + + for(size_t i = 0; i < queries.size(); i++) + { + /// execute all queries in the holder and pass the results + char const *sql = queries[i].first; + if(sql) m_holder->SetResult(i, db->Query(sql)); + } + + /// sync with the caller thread + m_queue->add(m_callback); +} diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h new file mode 100644 index 00000000000..0018a7ed08e --- /dev/null +++ b/src/shared/Database/SqlOperations.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SQLOPERATIONS_H +#define __SQLOPERATIONS_H + +#include "Common.h" + +#include "zthread/LockedQueue.h" +#include "zthread/FastMutex.h" +#include "zthread/Thread.h" +#include <queue> +#include "Utilities/Callback.h" + +/// ---- BASE --- + +class Database; +class SqlDelayThread; + +class SqlOperation +{ + public: + virtual void OnRemove() { delete this; } + virtual void Execute(Database *db) = 0; + virtual ~SqlOperation() {} +}; + +/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + +class SqlStatement : public SqlOperation +{ + private: + const char *m_sql; + public: + SqlStatement(const char *sql) : m_sql(strdup(sql)){} + ~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); } + void Execute(Database *db); +}; + +class SqlTransaction : public SqlOperation +{ + private: + std::queue<const char *> m_queue; + public: + SqlTransaction() {} + void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } + void Execute(Database *db); +}; + +/// ---- ASYNC QUERIES ---- + +class SqlQuery; /// contains a single async query +class QueryResult; /// the result of one +class SqlResultQueue; /// queue for thread sync +class SqlQueryHolder; /// groups several async quries +class SqlQueryHolderEx; /// points to a holder, added to the delay thread + +class SqlResultQueue : public ZThread::LockedQueue<MaNGOS::IQueryCallback*, ZThread::FastMutex> +{ + public: + SqlResultQueue() {} + void Update(); +}; + +class SqlQuery : public SqlOperation +{ + private: + const char *m_sql; + MaNGOS::IQueryCallback * m_callback; + SqlResultQueue * m_queue; + public: + SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) + : m_sql(strdup(sql)), m_callback(callback), m_queue(queue) {} + ~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); } + void Execute(Database *db); +}; + +class SqlQueryHolder +{ + friend class SqlQueryHolderEx; + private: + typedef std::pair<const char*, QueryResult*> SqlResultPair; + std::vector<SqlResultPair> m_queries; + public: + SqlQueryHolder() {} + ~SqlQueryHolder(); + bool SetQuery(size_t index, const char *sql); + bool SetPQuery(size_t index, const char *format, ...) ATTR_PRINTF(3,4); + void SetSize(size_t size); + QueryResult* GetResult(size_t index); + void SetResult(size_t index, QueryResult *result); + void Execute(MaNGOS::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue); +}; + +class SqlQueryHolderEx : public SqlOperation +{ + private: + SqlQueryHolder * m_holder; + MaNGOS::IQueryCallback * m_callback; + SqlResultQueue * m_queue; + public: + SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) + : m_holder(holder), m_callback(callback), m_queue(queue) {} + void Execute(Database *db); +}; +#endif //__SQLOPERATIONS_H diff --git a/src/shared/Database/dbcfile.cpp b/src/shared/Database/dbcfile.cpp new file mode 100644 index 00000000000..2521ab22614 --- /dev/null +++ b/src/shared/Database/dbcfile.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dbcfile.h" + +DBCFile::DBCFile() +{ + data = NULL; + fieldsOffset = NULL; +} + +bool DBCFile::Load(const char *filename, const char *fmt) +{ + + uint32 header; + if(data) + { + delete [] data; + data=NULL; + } + FILE * f=fopen(filename,"rb"); + if(!f)return false; + + fread(&header,4,1,f); // Number of records + EndianConvert(header); + if(header!=0x43424457) + { + //printf("not dbc file"); + return false; //'WDBC' + } + fread(&recordCount,4,1,f); // Number of records + EndianConvert(recordCount); + fread(&fieldCount,4,1,f); // Number of fields + EndianConvert(fieldCount); + fread(&recordSize,4,1,f); // Size of a record + EndianConvert(recordSize); + fread(&stringSize,4,1,f); // String size + EndianConvert(stringSize); + + fieldsOffset = new uint32[fieldCount]; + fieldsOffset[0] = 0; + for(uint32 i = 1; i < fieldCount; i++) + { + fieldsOffset[i] = fieldsOffset[i - 1]; + if (fmt[i - 1] == 'b' || fmt[i - 1] == 'X') // byte fields + fieldsOffset[i] += 1; + else // 4 byte fields (int32/float/strings) + fieldsOffset[i] += 4; + } + + data = new unsigned char[recordSize*recordCount+stringSize]; + stringTable = data + recordSize*recordCount; + fread(data,recordSize*recordCount+stringSize,1,f); + fclose(f); + return true; +} + +DBCFile::~DBCFile() +{ + if(data) + delete [] data; + if(fieldsOffset) + delete [] fieldsOffset; +} + +DBCFile::Record DBCFile::getRecord(size_t id) +{ + assert(data); + return Record(*this, data + id*recordSize); +} + +uint32 DBCFile::GetFormatRecordSize(const char * format,int32* index_pos) +{ + uint32 recordsize = 0; + int32 i = -1; + for(uint32 x=0; format[x];++x) + switch(format[x]) + { + case FT_FLOAT: + case FT_INT: + recordsize+=4; + break; + case FT_STRING: + recordsize+=sizeof(char*); + break; + case FT_SORT: + i=x; + break; + case FT_IND: + i=x; + recordsize+=4; + break; + case FT_BYTE: + recordsize += 1; + break; + } + + if(index_pos) + *index_pos = i; + + return recordsize; +} + +char* DBCFile::AutoProduceData(const char* format, uint32& records, char**& indexTable) +{ + /* + format STRING, NA, FLOAT,NA,INT <=> + struct{ + char* field0, + float field1, + int field2 + }entry; + + this func will generate entry[rows] data; + */ + + typedef char * ptr; + if(strlen(format)!=fieldCount) + return NULL; + + //get struct size and index pos + int32 i; + uint32 recordsize=GetFormatRecordSize(format,&i); + + if(i>=0) + { + uint32 maxi=0; + //find max index + for(uint32 y=0;y<recordCount;y++) + { + uint32 ind=getRecord(y).getUInt (i); + if(ind>maxi)maxi=ind; + } + + ++maxi; + records=maxi; + indexTable=new ptr[maxi]; + memset(indexTable,0,maxi*sizeof(ptr)); + } + else + { + records = recordCount; + indexTable = new ptr[recordCount]; + } + + char* dataTable= new char[recordCount*recordsize]; + + uint32 offset=0; + + for(uint32 y =0;y<recordCount;y++) + { + if(i>=0) + { + indexTable[getRecord(y).getUInt(i)]=&dataTable[offset]; + } + else + indexTable[y]=&dataTable[offset]; + + for(uint32 x=0;x<fieldCount;x++) + { + switch(format[x]) + { + case FT_FLOAT: + *((float*)(&dataTable[offset]))=getRecord(y).getFloat(x); + offset+=4; + break; + case FT_IND: + case FT_INT: + *((uint32*)(&dataTable[offset]))=getRecord(y).getUInt(x); + offset+=4; + break; + case FT_BYTE: + *((uint8*)(&dataTable[offset]))=getRecord(y).getUInt8(x); + offset+=1; + break; + case FT_STRING: + *((char**)(&dataTable[offset]))=NULL; // will be replaces non-empty or "" strings in AutoProduceStrings + offset+=sizeof(char*); + break; + } + } + } + + return dataTable; +} + +char* DBCFile::AutoProduceStrings(const char* format, char* dataTable) +{ + if(strlen(format)!=fieldCount) + return NULL; + + char* stringPool= new char[stringSize]; + memcpy(stringPool,stringTable,stringSize); + + uint32 offset=0; + + for(uint32 y =0;y<recordCount;y++) + { + for(uint32 x=0;x<fieldCount;x++) + switch(format[x]) + { + case FT_FLOAT: + case FT_IND: + case FT_INT: + offset+=4; + break; + case FT_BYTE: + offset+=1; + break; + case FT_STRING: + // fill only not filled entries + char** slot = (char**)(&dataTable[offset]); + if(!*slot || !**slot) + { + const char * st = getRecord(y).getString(x); + *slot=stringPool+(st-(const char*)stringTable); + } + offset+=sizeof(char*); + break; + } + } + + return stringPool; +} diff --git a/src/shared/Database/dbcfile.h b/src/shared/Database/dbcfile.h new file mode 100644 index 00000000000..bc59914ce53 --- /dev/null +++ b/src/shared/Database/dbcfile.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCFILE_H +#define DBCFILE_H +#include "Platform/Define.h" +#include "Utilities/ByteConverter.h" +#include <cassert> + +enum +{ + FT_NA='x', //not used or unknown, 4 byte size + FT_NA_BYTE='X', //not used or unknown, byte + FT_STRING='s', //char* + FT_FLOAT='f', //float + FT_INT='i', //uint32 + FT_BYTE='b', //uint8 + FT_SORT='d', //sorted by this field, field is not included + FT_IND='n', //the same,but parsed to data + FT_LOGIC='l' //Logical (boolean) +}; + +class DBCFile +{ + public: + DBCFile(); + ~DBCFile(); + + bool Load(const char *filename, const char *fmt); + + class Record + { + public: + float getFloat(size_t field) const + { + assert(field < file.fieldCount); + float val = *reinterpret_cast<float*>(offset+file.GetOffset(field)); + EndianConvert(val); + return val; + } + uint32 getUInt(size_t field) const + { + assert(field < file.fieldCount); + uint32 val = *reinterpret_cast<uint32*>(offset+file.GetOffset(field)); + EndianConvert(val); + return val; + } + uint8 getUInt8(size_t field) const + { + assert(field < file.fieldCount); + return *reinterpret_cast<uint8*>(offset+file.GetOffset(field)); + } + + const char *getString(size_t field) const + { + assert(field < file.fieldCount); + size_t stringOffset = getUInt(field); + assert(stringOffset < file.stringSize); + return reinterpret_cast<char*>(file.stringTable + stringOffset); + } + + private: + Record(DBCFile &file_, unsigned char *offset_): offset(offset_), file(file_) {} + unsigned char *offset; + DBCFile &file; + + friend class DBCFile; + + }; + + // Get record by id + Record getRecord(size_t id); + /// Get begin iterator over records + + uint32 GetNumRows() const { return recordCount;} + uint32 GetCols() const { return fieldCount; } + uint32 GetOffset(size_t id) const { return (fieldsOffset != NULL && id < fieldCount) ? fieldsOffset[id] : 0; } + bool IsLoaded() {return (data!=NULL);} + char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable); + char* AutoProduceStrings(const char* fmt, char* dataTable); + static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); + private: + + uint32 recordSize; + uint32 recordCount; + uint32 fieldCount; + uint32 stringSize; + uint32 *fieldsOffset; + unsigned char *data; + unsigned char *stringTable; +}; +#endif |