/* * CacheManager.cpp * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.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 (version 2 of the License) * 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 #include "util/log.h" #include "util/stringarray.h" #include "util/serverconsole.h" #include "cachemanager.h" #include "commandmanager.h" #include "questmanager.h" #include "util/eventmanager.h" #include "util/psdatabase.h" #include "globals.h" #include "bulkobjects/psraceinfo.h" #include "bulkobjects/psguildinfo.h" #include "bulkobjects/pstrade.h" #include "bulkobjects/psaccountinfo.h" #include "client.h" #include "bulkobjects/psquest.h" #include "bulkobjects/psmerchantinfo.h" #include "bulkobjects/psspell.h" #include "bulkobjects/psglyph.h" CacheManager::CacheManager() { slotMap[PSCHARACTER_SLOT_RIGHTHAND] = PSITEMSTATS_SLOT_RIGHTHAND; slotMap[PSCHARACTER_SLOT_LEFTHAND] = PSITEMSTATS_SLOT_LEFTHAND; slotMap[PSCHARACTER_SLOT_BOTHHANDS] = PSITEMSTATS_SLOT_BOTHHANDS; slotMap[PSCHARACTER_SLOT_HEAD] = PSITEMSTATS_SLOT_HEAD; slotMap[PSCHARACTER_SLOT_RIGHTFINGER] = PSITEMSTATS_SLOT_RIGHTFINGER; slotMap[PSCHARACTER_SLOT_LEFTFINGER] = PSITEMSTATS_SLOT_LEFTFINGER; slotMap[PSCHARACTER_SLOT_NECK] = PSITEMSTATS_SLOT_NECK; slotMap[PSCHARACTER_SLOT_BOOTS] = PSITEMSTATS_SLOT_BOOTS; slotMap[PSCHARACTER_SLOT_BACK] = PSITEMSTATS_SLOT_BACK; slotMap[PSCHARACTER_SLOT_ARMS] = PSITEMSTATS_SLOT_ARMS; slotMap[PSCHARACTER_SLOT_GLOVES] = PSITEMSTATS_SLOT_GLOVES; slotMap[PSCHARACTER_SLOT_LEGS] = PSITEMSTATS_SLOT_LEGS; slotMap[PSCHARACTER_SLOT_BELT] = PSITEMSTATS_SLOT_BELT; slotMap[PSCHARACTER_SLOT_BRACERS] = PSITEMSTATS_SLOT_BRACERS; slotMap[PSCHARACTER_SLOT_TORSO] = PSITEMSTATS_SLOT_TORSO; slotMap[PSCHARACTER_SLOT_MIND] = PSITEMSTATS_SLOT_MIND; psItemStatFlags statflag("MELEEWEAPON", PSITEMSTATS_FLAG_IS_A_MELEE_WEAPON); ItemStatFlagArray.Push(statflag); statflag.Set( "ARMOR", PSITEMSTATS_FLAG_IS_ARMOR); ItemStatFlagArray.Push(statflag); statflag.Set( "RANGEWEAPON", PSITEMSTATS_FLAG_IS_A_RANGED_WEAPON); ItemStatFlagArray.Push(statflag); statflag.Set( "SHIELD", PSITEMSTATS_FLAG_IS_A_SHIELD ); ItemStatFlagArray.Push(statflag); statflag.Set( "AMMO", PSITEMSTATS_FLAG_IS_AMMO ); ItemStatFlagArray.Push(statflag); statflag.Set( "CONTAINER", PSITEMSTATS_FLAG_IS_A_CONTAINER ); ItemStatFlagArray.Push(statflag); statflag.Set( "CANTRANSFORM", PSITEMSTATS_FLAG_CAN_TRANSFORM ); ItemStatFlagArray.Push(statflag); statflag.Set( "USESAMMO", PSITEMSTATS_FLAG_USES_AMMO ); ItemStatFlagArray.Push(statflag); statflag.Set( "STACKABLE", PSITEMSTATS_FLAG_IS_STACKABLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "EQUIP_STACKABLE", PSITEMSTATS_FLAG_IS_EQUIP_STACKABLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "GLYPH", PSITEMSTATS_FLAG_IS_GLYPH ); ItemStatFlagArray.Push(statflag); statflag.Set( "TRIA", PSITEMSTATS_FLAG_TRIA ); ItemStatFlagArray.Push(statflag); statflag.Set( "HEXA", PSITEMSTATS_FLAG_HEXA ); ItemStatFlagArray.Push(statflag); statflag.Set( "OCTA", PSITEMSTATS_FLAG_OCTA ); ItemStatFlagArray.Push(statflag); statflag.Set( "CIRCLE", PSITEMSTATS_FLAG_CIRCLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "CONSUMABLE", PSITEMSTATS_FLAG_CONSUMABLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "READABLE", PSITEMSTATS_FLAG_IS_READABLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "WRITEABLE", PSITEMSTATS_FLAG_IS_WRITEABLE ); ItemStatFlagArray.Push(statflag); statflag.Set( "END",0 ); ItemStatFlagArray.Push(statflag); effectID = 0; } CacheManager::~CacheManager() { UnloadAll(); } bool CacheManager::PreloadAll() { maxCommonStrID = db->SelectSingleNumber("SELECT MAX(id) FROM common_strings"); if (!PreloadCommonStrings()) return false; if (!PreloadSectors()) return false; if (!PreloadSkills()) return false; if (!PreloadRaceInfo()) return false; if (!PreloadTraits()) return false; // Need RaceInfo if (!PreloadItemCategories()) return false; if (!PreloadItemAnimList()) return false; if (!PreloadItemStatsDatabase()) return false; if (!PreloadWays()) return false; if (!PreloadSpells()) return false; if (!PreloadQuests()) return false; if (!PreloadTradeCombinations()) return false; if (!PreloadTradeTransformations()) return false; if (!PreloadTradeProcesses()) return false; if (!PreloadTradePatterns()) return false; if (!PreloadTips()) return false; if (!PreloadBadNames()) return false; if (!PreloadArmorVsWeapon()) return false; if (!PreloadMovement()) return false; // PreCreateCraftMessages(); PreloadCommandGroups(); PreloadUpdateInfo(); return true; } void CacheManager::PreloadCommandGroups() { commandManager = new psCommandManager; commandManager->LoadFromDatabase(); } void CacheManager::PreCreateCraftMessages() { /// Get a list of all the trade patterns in the database. Result allPatterns(db->Select("SELECT * from trade_patterns") ); int currentPattern = 0; psMsgCraftingInfo * tradePattern; for (currentPattern=0; currentPattern < (int)allPatterns.Count(); currentPattern++) { int patternID = allPatterns[currentPattern].GetInt("id"); int groupID = allPatterns[currentPattern].GetInt("group_id"); csString patternName = allPatterns[currentPattern]["pattern_name"]; /// Create the new pattern message for it. tradePattern = new psMsgCraftingInfo( patternID, patternName ); CacheCraftCombos( tradePattern, patternID, groupID ); CacheCraftTransforms( tradePattern, patternID, groupID ); tradePattern->Construct(); craftingMessages.Push( tradePattern ); } } psMsgCraftingInfo* CacheManager::GetCraftInfo( int patternID ) { for ( size_t z = 0; z < craftingMessages.GetSize(); z++ ) { if ( craftingMessages[z]->tradePatternID == patternID ) return craftingMessages[z]; } return NULL; } void CacheManager::UnloadAll() { delete commandManager; { csHash::GlobalIterator it (quests_by_id.GetIterator ()); quests_by_id.Empty(); while (it.HasNext ()) { psQuest* quest = it.Next (); delete quest; } } { csHash::GlobalIterator it (alliance_by_id.GetIterator ()); while (it.HasNext ()) { psGuildAlliance* alliance = it.Next (); delete alliance; } } { csHash::GlobalIterator it (guildinfo_by_id.GetIterator ()); while (it.HasNext ()) { psGuildInfo* guildinfo = it.Next (); delete guildinfo; } } { csHash::GlobalIterator it (itemStats_NameHash.GetIterator ()); while (it.HasNext ()) { psItemStats* itemstats = it.Next (); delete itemstats; } } { csHash::GlobalIterator it (sectorinfo_by_id.GetIterator ()); while (it.HasNext ()) { psSectorInfo* sector = it.Next (); delete sector; } } // ToDo: unload everything else } void CacheManager::RemoveInstance( psItem * & item ) { Notify2(LOG_CACHE, "Removing Instance of item: %u", item->GetUID()); if (item->GetUID() != 0) db->Command("DELETE from item_instances where id='%u'", item->GetUID()); delete item; item = NULL; } bool CacheManager::PreloadSkills() { unsigned int currentrow; psSkillInfo *newskill; Result result(db->Select("SELECT * from skills") ); if (!result.IsValid()) return false; for (currentrow=0; currentrowid = (PSSKILL) result[currentrow].GetInt("skill_id"); newskill->name = result[currentrow]["name"]; newskill->description = result[currentrow]["description"]; newskill->practice_factor = result[currentrow].GetInt("practice_factor"); newskill->mental_factor = result[currentrow].GetInt("mental_factor"); newskill->price = psMoney(result[currentrow].GetInt("price")); newskill->baseCost = result[currentrow].GetInt("base_rank_cost"); csString type( result[currentrow]["category"] ); if ( type == "STATS" ) newskill->category = PSSKILLS_CATEGORY_STATS; else if ( type == "COMBAT" ) newskill->category = PSSKILLS_CATEGORY_COMBAT; else if ( type == "MAGIC" ) newskill->category = PSSKILLS_CATEGORY_MAGIC; else if ( type == "JOBS" ) newskill->category = PSSKILLS_CATEGORY_JOBS; else if ( type == "VARIOUS" ) newskill->category = PSSKILLS_CATEGORY_VARIOUS; skillinfolist.Push(newskill); maxCommonStrID++; msg_strings.Register(newskill->name,(csStringID)maxCommonStrID); } } return true; } bool CacheManager::PreloadSectors() { unsigned int currentrow; psSectorInfo *newsector; Result result(db->Select("SELECT * from sectors") ); if (!result.IsValid()) return false; for (currentrow=0; currentrow< result.Count(); currentrow++) { newsector=new psSectorInfo; CS_ASSERT(newsector!=NULL); newsector->uid = result[currentrow].GetInt("id"); newsector->name = result[currentrow]["name"]; newsector->rain_enabled = strcmp(result[currentrow]["rain_enabled"],"Y")==0; newsector->rain_min_gap = result[currentrow].GetInt("rain_min_gap"); newsector->rain_max_gap = result[currentrow].GetInt("rain_max_gap"); CS_ASSERT(newsector->rain_min_gap <= newsector->rain_max_gap); newsector->rain_min_duration = result[currentrow].GetInt("rain_min_duration"); newsector->rain_max_duration = result[currentrow].GetInt("rain_max_duration"); CS_ASSERT(newsector->rain_min_duration <= newsector->rain_max_duration); newsector->rain_min_drops = result[currentrow].GetInt("rain_min_drops"); newsector->rain_max_drops = result[currentrow].GetInt("rain_max_drops"); CS_ASSERT(newsector->rain_min_drops <= newsector->rain_max_drops); newsector->lightning_min_gap = result[currentrow].GetInt("lightning_min_gap"); newsector->lightning_max_gap = result[currentrow].GetInt("lightning_max_gap"); CS_ASSERT(newsector->lightning_min_gap <= newsector->lightning_max_gap); newsector->rain_min_fade_in = result[currentrow].GetInt("rain_min_fade_in"); newsector->rain_max_fade_in = result[currentrow].GetInt("rain_max_fade_in"); CS_ASSERT(newsector->rain_min_fade_in <= newsector->rain_max_fade_in); newsector->rain_min_fade_out = result[currentrow].GetInt("rain_min_fade_out"); newsector->rain_max_fade_out = result[currentrow].GetInt("rain_max_fade_out"); CS_ASSERT(newsector->rain_min_fade_out <= newsector->rain_max_fade_out); sectorinfo_by_id.Put(newsector->uid,newsector); sectorinfo_by_name.Put(csHashCompute(newsector->name),newsector); maxCommonStrID++; msg_strings.Register(newsector->name,(csStringID)maxCommonStrID); } return true; } bool CacheManager::PreloadMovement() { Result modes(db->Select("SELECT * FROM movement_modes")); if ( !modes.IsValid() || !modes.Count() ) return false; for (unsigned int i=0; iid = modes[i].GetUInt32("id"); newmode->name = modes[i]["name"]; newmode->move_mod.x = modes[i].GetFloat("move_mod_x"); newmode->move_mod.y = modes[i].GetFloat("move_mod_y"); newmode->move_mod.z = modes[i].GetFloat("move_mod_z"); newmode->rotate_mod.x = modes[i].GetFloat("rotate_mod_x"); newmode->rotate_mod.y = modes[i].GetFloat("rotate_mod_y"); newmode->rotate_mod.z = modes[i].GetFloat("rotate_mod_z"); newmode->idle_animation = modes[i]["idle_animation"]; if (newmode->id > 127) // Based on DR message variable size { Error3("ID %u for movement '%s' is to large.\n" "Clients only support up to 128 different movement types, with IDs from 0-127", newmode->id, newmode->name.GetData() ); return false; } char_modes.Put(newmode->id,newmode); } Result types(db->Select("SELECT * FROM movement_types")); if ( !types.IsValid() || !types.Count() ) return false; for (unsigned int i=0; iid = types[i].GetUInt32("id"); newmove->name = types[i]["name"]; newmove->base_move.x = types[i].GetFloat("base_move_x"); newmove->base_move.y = types[i].GetFloat("base_move_y"); newmove->base_move.z = types[i].GetFloat("base_move_z"); newmove->base_rotate.x = types[i].GetFloat("base_rotate_x"); newmove->base_rotate.y = types[i].GetFloat("base_rotate_y"); newmove->base_rotate.z = types[i].GetFloat("base_rotate_z"); if (newmove->id > 31) // Based on active move bits in client movement manager { Error3("ID %u for movement '%s' is to large.\n" "Clients only support up to 32 different movement types, with IDs from 0-31", newmove->id, newmove->name.GetData() ); return false; } movements.Put(newmove->id,newmove); } return true; } uint8_t CacheManager::GetCharModeID(const char* name) { for (size_t i=0; iname == name) return (uint8_t)i; return (uint8_t)-1; } uint8_t CacheManager::GetMovementID(const char* name) { for (size_t i=0; iname == name) return (uint8_t)i; return (uint8_t)-1; } bool CacheManager::PreloadArmorVsWeapon() { unsigned int currentrow; Result result(db->Select("select * from armor_vs_weapon")); if (!result.IsValid()) return false; for (currentrow=0; currentrowid = result[currentrow].GetUInt32("id"); newvs->c[0][0] = result[currentrow].GetFloat("1a"); newvs->c[0][1] = result[currentrow].GetFloat("1b"); newvs->c[0][2] = result[currentrow].GetFloat("1c"); newvs->c[0][3] = result[currentrow].GetFloat("1d"); newvs->c[1][0] = result[currentrow].GetFloat("2a"); newvs->c[1][1] = result[currentrow].GetFloat("2b"); newvs->c[1][2] = result[currentrow].GetFloat("2c"); newvs->c[2][0] = result[currentrow].GetFloat("3a"); newvs->c[2][1] = result[currentrow].GetFloat("3b"); newvs->c[2][2] = result[currentrow].GetFloat("3c"); newvs->weapontype = result[currentrow]["weapon_type"]; armor_vs_weapon.Push(newvs); } Notify2(LOG_COMBAT,"Testing Armor VS Weapon table ('3c','Dagger'): %f\n",GetArmorVSWeaponResistance("3c","Dagger")); Notify2(LOG_COMBAT,"Testing Armor VS Weapon table ('1d','Claymore'): %f\n",GetArmorVSWeaponResistance("1d","Claymore")); Notify2(LOG_COMBAT,"Testing Armor VS Weapon table ('2b','Sabre'): %f\n",GetArmorVSWeaponResistance("2b","Sabre")); return true; } bool CacheManager::PreloadCommonStrings() { unsigned int currentrow; Result result(db->Select("select id,string from common_strings")); if (!result.IsValid()) return false; for (currentrow=0; currentrowSelect("select * from quests order by id")); if (!result.IsValid()) return false; for (currentrow=0; currentrowLoad(result[currentrow])) quests_by_id.Put(quest->GetID(),quest); else delete quest; } // Process loaded quests csHash::GlobalIterator it (quests_by_id.GetIterator ()); while (it.HasNext ()) { psQuest* quest = it.Next (); quest->PostLoad(); } return true; } csHash::GlobalIterator CacheManager::GetQuestIterator() { return quests_by_id.GetIterator(); } bool CacheManager::UnloadQuest(int id) { bool ret = false; psQuest* quest = quests_by_id.Get(id, NULL); if(quest) { delete quest; quests_by_id.DeleteAll(id); ret = true; } else CPrintf(CON_ERROR, "Cannot find quest to remove.\n"); return ret; } bool CacheManager::LoadQuest(int id) { if(quests_by_id.Get(id, NULL)) { CPrintf(CON_ERROR, "Quest already exists.\n", id); return false; } psQuest *quest; Result result(db->Select("select * from quests where id=%d", id)); if (!result.IsValid() || result.Count() == 0) { CPrintf(CON_ERROR, "Cannot find quest %d in database.\n", id); return false; } quest = new psQuest; if (quest->Load(result[0])) quests_by_id.Put(quest->GetID(),quest); else { delete quest; return false; } // Process loaded quest if(!quest->PostLoad()) CPrintf(CON_ERROR, "Could not quest prerequisites"); // Load scripts if(!psserver->questmanager->LoadQuestScript(id)) CPrintf(CON_ERROR, "Could not load quest script for quest %d\n", id); return true; } //////////////////////////////////////////////////////////////////// // Trade Combinations bool CacheManager::PreloadTradeCombinations() { unsigned int currentrow; uint32 lastarrayid = 0; uint32 lastitemid = 0; //csPDelArray *newarray; Result result(db->Select( "select * from trade_combinations order by pattern_id, result_id, item_id")); if (!result.IsValid()) return false; CombinationConstruction *ctr = NULL; CombinationSet *set = NULL; for (currentrow=0; currentrowpatternID = id; combinations.Push(set); lastarrayid = id; } // If it is a new item start a new construction set for it. uint32 resultItem = result[currentrow].GetUInt32("result_id"); if ( resultItem != lastitemid ) { ctr = new CombinationConstruction; ctr->resultItem = resultItem; ctr->resultQuantity = result[currentrow].GetInt("result_qty"); set->set.Push(ctr); lastitemid = resultItem; } // Load the combination and push it into it's construction set. psTradeCombinations* comb = new psTradeCombinations; if (comb->Load(result[currentrow])) ctr->combinations.Push(comb); else delete comb; } return true; } // TODO: Make this faster with hash tables CombinationSet * CacheManager::FindCombinationsList(uint32 patternid) { for (size_t x=0; x < combinations.Length(); x++) { if (combinations[x]->patternID == patternid) return combinations[x]; } return NULL; } // Trade Transformations bool CacheManager::PreloadTradeTransformations() { unsigned int currentrow; uint32 lastpid = (uint32)-1; uint32 lastiid = (uint32)-1; csPDelArray *newarray; Result result(db->Select("select * from trade_transformations order by pattern_id, item_id")); if (!result.IsValid()) return false; for (currentrow=0; currentrow; transformations_list.Push(newarray); lastpid = pid; lastiid = iid; } psTradeTransformations* tran = new psTradeTransformations; if (tran->Load(result[currentrow])) newarray->Push(tran); else delete tran; } return true; } // TODO: Make this faster with hash tables csPDelArray *CacheManager::FindTransformationsList(uint32 patternid, uint32 targetid) { for (size_t x=0; xGet(0)->GetPatternId() == patternid) && (transformations_list[x]->Get(0)->GetItemId() == targetid) ) return transformations_list[x]; } return NULL; } // Trade Processes bool CacheManager::PreloadTradeProcesses() { unsigned int currentrow; psTradeProcesses* newProcess; // Get a list of the trade processes Result result(db->Select("select * from trade_processes order by process_id")); if (!result.IsValid()) return false; for (currentrow=0; currentrowLoad(result[currentrow])) { delete newProcess; return false; } tradeProcesses_IDHash.Put(newProcess->GetProcessId(),newProcess); } return true; } psTradeProcesses *CacheManager::GetTradeProcessesByID(uint32 id) { return tradeProcesses_IDHash.Get(id,NULL); } // Trade Patterns bool CacheManager::PreloadTradePatterns() { unsigned int currentrow; psTradePatterns* newPattern; // Get a list of the trade patterns ignoring the group and dummy ones Result result(db->Select("select * from trade_patterns where designitem_id != 0 order by designitem_id")); if (!result.IsValid()) return false; for (currentrow=0; currentrowLoad(result[currentrow])) { delete newPattern; return false; } tradePatterns_IDHash.Put(newPattern->GetDesignItemId(),newPattern); } return true; } psTradePatterns *CacheManager::GetTradePatternByItemID(uint32 id) { return tradePatterns_IDHash.Get(id,NULL); } /* psTradePatterns* CacheManager::InstantiateTradePatternByID(uint32 patternid) { Result result(db->Select("SELECT * from trade_patterns where id=%u",patternid)); if (!result.IsValid() || result.Count()<1) { Warning3(LOG_CONNECTIONS,"Could not find trade pattern for id %u. Error: %s", patternid,db->GetLastError()); return NULL; } psTradePatterns *patterninfo = new psTradePatterns; if (patterninfo->Load(result[0])) return patterninfo; else { delete patterninfo; return NULL; } } psTradePatterns* CacheManager::InstantiateTradePatternByDesignID(uint32 designid) { Result result(db->Select("SELECT * from trade_patterns where designitem_Id=%u",designid)); if (!result.IsValid() || result.Count()<1) { Warning3(LOG_CONNECTIONS,"Could not find trade pattern for design id %u. Error: %s", designid,db->GetLastError()); return NULL; } psTradePatterns *patterninfo = new psTradePatterns; if (patterninfo->Load(result[0])) return patterninfo; else { delete patterninfo; return NULL; } } */ /* psTradeAutoContainers* CacheManager::InstantiateTradeAutoContainerByID(uint32 container_ID) { Result result(db->Select("SELECT * from trade_autocontainers where item_instance_id=%d", container_ID ) ); if (!result.IsValid() || result.Count()<1) { Warning4(LOG_CONNECTIONS,"Could not find trade auto container for item ID %d. Error: %s Query: %s", container_ID, db->GetLastError(), db->GetLastQuery()); return 0; } psTradeAutoContainers *autocontainerinfo = new psTradeAutoContainers; if (autocontainerinfo->Load(result[0])) { return autocontainerinfo; } else { delete autocontainerinfo; return NULL; } } */ //////////////////////////////////////////////////////////////////// bool CacheManager::PreloadTips() { unsigned int currentrow; // Id<1000 means we are excluding Tutorial tips Result result(db->Select("select tip from tips where id<1000")); if (!result.IsValid()) return false; for (currentrow=0; currentrow::GlobalIterator it (quests_by_id.GetIterator ()); while (it.HasNext ()) { psQuest* quest = it.Next (); if (!strcmp(quest->GetName(),name)) return quest; } return NULL; } psQuest *CacheManager::AddDynamicQuest(const char *name, psQuest *parentQuest, int step) { if (!name) return NULL; psQuest *ptr; // subquests need a fixed id to be loaded at next restart // quest_id*10000+sub_id int id = 10000+(parentQuest->GetID()*100)+step; ptr = new psQuest; ptr->Init(id,name); ptr->SetTask("NULL"); ptr->SetParentQuest(parentQuest); parentQuest->AddSubQuest(id); quests_by_id.Put(id, ptr); return ptr; } // TODO: This should be done faster, probably not with an array psSkillInfo *CacheManager::GetSkillByID(unsigned int id) { size_t i; psSkillInfo *currentskill; for (i=0;iid==id) return currentskill; } return NULL; } // TODO: This should be done faster, probably not with an array psSkillInfo *CacheManager::GetSkillByName(const char *name) { size_t i; psSkillInfo *currentskill; for (i=0;iname.CompareNoCase(name) ) return currentskill; } return NULL; } void CacheManager::GetSkillsListbyCategory(csArray & listskill,int category ) { psSkillInfo *currentskill; for(size_t i=0;icategory == category) listskill.Push(*currentskill); } } csHash::GlobalIterator CacheManager::GetSectorIterator() { return sectorinfo_by_name.GetIterator(); } psSectorInfo *CacheManager::GetSectorInfoByID(unsigned int id) { return sectorinfo_by_id.Get(id, NULL); } psSectorInfo *CacheManager::GetSectorInfoByName(const char *name) { if (name == NULL || strlen(name) == 0) return NULL; return sectorinfo_by_name.Get(csHashCompute(name), NULL); } PSTRAIT_LOCATION CacheManager::ConvertTraitLocationString(const char *locationstring) { if (locationstring==NULL) return PSTRAIT_LOCATION_NONE; for (int i = 0; i < PSTRAIT_LOCATION_COUNT; i++) { if (!strcasecmp(locationstring,psTrait::locationString[i])) return (PSTRAIT_LOCATION)i; } return PSTRAIT_LOCATION_NONE; } bool CacheManager::PreloadTraits() { unsigned int currentrow; psTrait *newtrait; Result result(db->Select("SELECT * from traits order by id")); if (!result.IsValid()) return false; for (currentrow=0; currentrowuid = result[currentrow].GetUInt32("id"); newtrait->next_trait_uid = result[currentrow].GetUInt32("next_trait"); newtrait->raceID = result[currentrow].GetUInt32("race_id"); newtrait->name = result[currentrow]["name"]; newtrait->cstr_id_mesh = result[currentrow].GetUInt32("cstr_id_mesh"); newtrait->cstr_id_material = result[currentrow].GetUInt32("cstr_id_material"); newtrait->cstr_id_texture = result[currentrow].GetUInt32("cstr_id_texture"); newtrait->onlyNPC = result[currentrow].GetInt("only_npc") != 0; newtrait->shaderVar = result[currentrow]["shader"]; psRaceInfo * raceInfo = GetRaceInfoByID(newtrait->raceID); if (raceInfo == NULL) { Error3("Trait (%u) references unresolvable race %s.", newtrait->uid,result[currentrow]["race_id"]); delete newtrait; continue; } newtrait->race = raceInfo->race; newtrait->gender = raceInfo->gender; loc=ConvertTraitLocationString(result[currentrow]["location"]); if (loc==PSTRAIT_LOCATION_NONE) { Error3("Trait (%u) references unresolvable location %s.", newtrait->uid,result[currentrow]["location"]); delete newtrait; continue; } newtrait->location = loc; traitlist.Push(newtrait); } // Update cross ref to next_trait for (size_t i = 0; i < traitlist.Length(); i++) { unsigned int next_uid = traitlist[i]->next_trait_uid; traitlist[i]->next_trait = GetTraitByID(next_uid); } Notify2( LOG_STARTUP, "%d Traits Loaded", traitlist.Length() ); return true; } // TODO: This should be done faster, probably not with an array psTrait *CacheManager::GetTraitByID(unsigned int id) { size_t i; psTrait *currenttrait; for (i=0;iuid==id) return currenttrait; } return NULL; } // TODO: This should be done faster, probably not with an array psTrait *CacheManager::GetTraitByName(const char *name) { /* int i; psTrait *currenttrait; for (i=0;iname == name) return currenttrait; } */ return NULL; } CacheManager::TraitIterator CacheManager::GetTraitIterator() { return traitlist.GetIterator(); } PSCHARACTER_GENDER CacheManager::ConvertGenderString(const char *genderstring) { if (genderstring==NULL) return PSCHARACTER_GENDER_NONE; switch (genderstring[0]) { case 'M': case 'm': return PSCHARACTER_GENDER_MALE; case 'F': case 'f': return PSCHARACTER_GENDER_FEMALE; case 'N': case 'n': return PSCHARACTER_GENDER_NONE; }; return PSCHARACTER_GENDER_NONE; } bool CacheManager::PreloadRaceInfo() { unsigned int currentrow; psRaceInfo *newraceinfo; Result result(db->Select("SELECT * from race_info")); if (!result.IsValid()) return false; for (currentrow=0; currentrowLoad(result[currentrow])) raceinfolist.Push(newraceinfo); else { delete newraceinfo; } } return true; } psRaceInfo *CacheManager::GetRaceInfoByIndex(int idx) { if (idx<0 || (size_t)idx>=raceinfolist.Length()) return NULL; return raceinfolist.Get(idx); } // TODO: This should be done faster, probably not with an array psRaceInfo *CacheManager::GetRaceInfoByID(unsigned int id) { size_t i; psRaceInfo *currentri; for (i=0;iuid==id) return currentri; } return NULL; } // TODO: This should be done faster, probably not with an array psRaceInfo *CacheManager::GetRaceInfoByNameGender(const char *name,PSCHARACTER_GENDER gender) { size_t i; psRaceInfo *currentri; for (i=0;igender==gender && currentri->name ==name) return currentri; } return NULL; } // TODO: This should be done faster, probably not with an array psRaceInfo *CacheManager::GetRaceInfoByNameGender( PSCHARACTER_RACE id, PSCHARACTER_GENDER gender) { size_t i; psRaceInfo *currentri; for (i=0;irace==id && currentri->gender==gender) return currentri; } return NULL; } psItemCategory *CacheManager::GetItemCategoryByID(unsigned int id) { size_t i; for (i=0;iid) return currentCategory; } return NULL; } psItemCategory *CacheManager::GetItemCategoryByName(const csString & name) { size_t i; for (i=0;iname) return currentCategory; } return NULL; } // TODO: This function needs to be implemented in a fast fashion psWay *CacheManager::GetWayByID(unsigned int id) { size_t i; for (i=0;iid) return currentWay; } return NULL; } // TODO: This function needs to be implemented in a fast fashion psWay *CacheManager::GetWayByName(const csString & name) { size_t i; for (i=0;iname) return currentWay; } return NULL; } // TODO: This function needs to be implemented in a fast fashion psSpell *CacheManager::GetSpellByID(unsigned int id) { size_t i; for (i=0;iGetID()) return currentSpell; } return NULL; } // TODO: This function needs to be implemented in a fast fashion psSpell *CacheManager::GetSpellByName(const csString & name) { size_t i; for (i=0;iGetName())) return currentSpell; } return NULL; } CacheManager::SpellIterator CacheManager::GetSpellIterator() { return spellList.GetIterator(); } // Get item basic stats by hashed table psItemStats *CacheManager::GetBasicItemStatsByName(csString name) { psItemStats *itemstats = itemStats_NameHash.Get(name,NULL); if(itemstats) return itemstats; bool loaded; csString escape; db->Escape( escape, name ); Result result(db->Select("SELECT * from item_stats where stat_type='B' and name='%s'", (const char *) name)); if (!result.IsValid() || result.Count() == 0) return NULL; itemstats = new psItemStats; loaded = itemstats->ReadItemStats(result[0]); // Prevent id conflicts if (!loaded|| itemStats_IDHash.Get(itemstats->GetUID(),NULL)) { if(loaded) CPrintf(CON_ERROR, "Duplicate item_stats ID where id='%u' found.\n", itemstats->GetUID()); delete itemstats; return NULL; } else { CS_ASSERT( itemstats->GetUID() != 0 ); itemStats_IDHash.Put(itemstats->GetUID(),itemstats); itemStats_NameHash.Put(itemstats->GetName(),itemstats); } return itemstats; } // Get item basic stats by hashed table psItemStats *CacheManager::GetBasicItemStatsByID(uint32 id) { psItemStats *itemstats = itemStats_IDHash.Get(id,NULL); if(itemstats) return itemstats; Result result(db->Select("SELECT * from item_stats where stat_type='B' and id='%u'", id)); if (!result.IsValid() || result.Count() == 0) return NULL; itemstats = new psItemStats; bool loaded = itemstats->ReadItemStats(result[0]); // Prevent name conflicts if (!loaded || itemStats_NameHash.Get(itemstats->GetName(),NULL)) { if(loaded) CPrintf(CON_ERROR, "Duplicate item_stats name where name='%s' found.\n", itemstats->GetName()); delete itemstats; return NULL; } else { CS_ASSERT( itemstats->GetUID() != 0 ); itemStats_IDHash.Put(itemstats->GetUID(),itemstats); itemStats_NameHash.Put(itemstats->GetName(),itemstats); } return itemstats; } void CacheManager::GetTipByID(int id, csString& tip) { if ((size_t)id>=tips_list.Length()) { tip = ""; return; } tip = tips_list.Get(id); } unsigned int CacheManager::GetTipLength() { return (unsigned int)tips_list.Length(); } /* psItemStats *CacheManager::GetBasicItemStatsByIndex(unsigned int idx) { if (idx>(unsigned int)basicitemstatslist.Length()) return NULL; return basicitemstatslist.Get(idx); } */ const char* CacheManager::Attribute2String( PSITEMSTATS_STAT s ) { switch ( s ) { case PSITEMSTATS_STAT_STRENGTH: return "STRENGTH"; case PSITEMSTATS_STAT_AGILITY: return "AGILITY"; case PSITEMSTATS_STAT_ENDURANCE: return "ENDURANCE"; case PSITEMSTATS_STAT_INTELLIGENCE: return "INTELLIGENCE"; case PSITEMSTATS_STAT_WILL: return "WILL"; case PSITEMSTATS_STAT_CHARISMA: return "CHARISMA"; default: return "None"; } } PSITEMSTATS_STAT CacheManager::ConvertAttributeString(const char *attributestring) { if (attributestring==NULL) return PSITEMSTATS_STAT_NONE; if (!strcasecmp(attributestring,"STRENGTH") || !strcasecmp(attributestring,"STR")) return PSITEMSTATS_STAT_STRENGTH; if (!strcasecmp(attributestring,"AGILITY") || !strcasecmp(attributestring,"AGI")) return PSITEMSTATS_STAT_AGILITY; if (!strcasecmp(attributestring,"ENDURANCE") || !strcasecmp(attributestring,"END") ) return PSITEMSTATS_STAT_ENDURANCE; if (!strcasecmp(attributestring,"INTELLIGENCE") || !strcasecmp(attributestring,"INT")) return PSITEMSTATS_STAT_INTELLIGENCE; if (!strcasecmp(attributestring,"WILL") || !strcasecmp(attributestring,"WIL")) return PSITEMSTATS_STAT_WILL; if (!strcasecmp(attributestring,"CHARISMA") || !strcasecmp(attributestring,"CHA")) return PSITEMSTATS_STAT_CHARISMA; return PSITEMSTATS_STAT_NONE; } PSSKILL CacheManager::ConvertSkillString(const char *skillstring) { if (skillstring==NULL) return PSSKILL_NONE; psSkillInfo *skillinfo=GetSkillByName(skillstring); if (skillinfo==NULL) return PSSKILL_NONE; return skillinfo->id; } PSSKILL CacheManager::ConvertSkill(int skill_id) { if (skill_id >= (int)PSSKILL_NONE && skill_id < (int)PSSKILL_COUNT) { return (PSSKILL)skill_id; } return PSSKILL_NONE; } bool CacheManager::PreloadItemCategories() { Result categories(db->Select("SELECT * from item_categories")); if (categories.IsValid()) { int i,count=categories.Count(); for (i=0;iid = categories[i].GetInt("category_id"); category->name = categories[i]["name"]; category->repair_tool_stat_id = categories[i].GetInt("item_stat_id_repair_tool"); const char *flag = categories[i]["is_repair_tool_consumed"]; category->repair_tool_consumed = (flag && flag[0]=='Y'); category->repair_skill_id = categories[i].GetInt("skill_id_repair"); itemCategoryList.Push(category); } } return true; } bool CacheManager::PreloadWays() { Result ways(db->Select("SELECT * from ways")); if (ways.IsValid()) { int i,count=ways.Count(); for (i=0;iid = atoi(ways[i]["id"]); way->name = ways[i]["name"]; if (way->name == "Crystal") { way->skill = PSSKILL_CRYSTALWAY; way->related_stat = PSSKILL_CHA; } else if (way->name == "Azure") { way->skill = PSSKILL_AZUREWAY; way->related_stat = PSSKILL_WILL; } else if (way->name == "Red") { way->skill = PSSKILL_REDWAY; way->related_stat = PSSKILL_WILL; } else if (way->name == "Dark") { way->skill = PSSKILL_DARKWAY; way->related_stat = PSSKILL_CHA; } else if (way->name == "Brown") { way->skill = PSSKILL_BROWNWAY; way->related_stat = PSSKILL_INT; } else if (way->name == "Blue") { way->skill = PSSKILL_BLUEWAY; way->related_stat = PSSKILL_INT; } else { Error2("Unknown WAY: %s",way->name.GetData()); } wayList.Push(way); } } return true; } bool CacheManager::PreloadSpells() { Result spells(db->Select("SELECT * from spells")); if (spells.IsValid()) { int i,count=spells.Count(); for (i=0;iLoad(spells[i])) spellList.Push(spell); else delete spell; } } return true; } csPDelArray *CacheManager::FindAnimationList(int id) { for (size_t x=0; xGet(0)->id == id) return item_anim_list[x]; } return NULL; } bool CacheManager::PreloadItemAnimList() { unsigned int currentrow,lastarrayid=0; psItemAnimation *newitem; csPDelArray *newarray; Result result(db->Select("SELECT * from item_animations order by id, min_use_level")); if (!result.IsValid()) return false; for (currentrow=0; currentrow; item_anim_list.Push(newarray); lastarrayid = id; } newitem = new psItemAnimation; newitem->id = id; newitem->anim_name = FindCommonString( result[currentrow].GetInt("cstr_id_animation") ); newitem->anim_id = csInvalidStringID; newitem->flags = result[currentrow].GetInt("type_flags"); newitem->min_level_required = result[currentrow].GetInt("min_use_level"); newarray->Push(newitem); } return true; } psItemSet *CacheManager::LoadWorldItems(psSectorInfo *sector,int &loadeditems) { uint32 parentid; loadeditems=0; //Result result(sector ? // db->Select("SELECT * from item_instances where loc_sector_id='%u' and char_id_owner=0 or char_id_owner is NULL or parent_item_id=!0",sector->uid) : // db->Select("SELECT * from item_instances where char_id_owner=0 or char_id_owner is NULL or parent_item_id=!0")); Result result(sector ? db->Select("SELECT * from item_instances where loc_sector_id='%u'",sector->uid) : db->Select("SELECT * from item_instances where loc_sector_id is NOT NULL")); /* Our approach here is to allocate item instance data for every item instance returned. * On our first pass we do not resolve any parent item references. Instead we store the parent id * in an array. * On the second pass we resolve all parent references. * A tree would be faster, but since this is only done on world start * it's not too much of a big deal. */ csArray containers; psItemSet *itemset = new psItemSet; // Load items if ( result.IsValid() ) { int i, count=result.Count(); for (i=0;iGetIsGlyph()) { item = new psGlyph(); } else { item = new psItem(); } if (!item->Load(result[i],parentid)) { Error2("Error in LoadWorldItems! Item instance %u could not be loaded. Skipping.\n",result[i].GetUInt32("id")); delete item; continue; } item->SetCreator(ITEM_CACHE); // This item is in another item. Update the parent item id list and then move on. if ( item->GetParentID() && !item->GetLocInParent() ) { // Field missing. Needed for items in other items. Error2("Item with id %s has parent item without location in parent.\n",result[i]["id"]); delete item; continue; } if ( item->GetIsContainer() ) { containers.Push(item->GetUID()); } loadeditems++; itemset->Add(item,parentid); } /* for ( size_t z = 0; z < containers.Length(); z++ ) { Result result( db->Select("SELECT * from item_instances where parent_item_id=%u",containers[z])); if ( result.IsValid() ) { int i, count=result.Count(); for (i=0;iGetIsGlyph()) { item = new psGlyph(); } else { item = new psItem(); } if (!item->Load(result[i],parentid)) { Error2("Error in LoadWorldItems! Item instance %u could not be loaded. Skipping.\n",result[i].GetUInt32("id")); delete item; continue; } item->SetCreator(ITEM_CACHE); // This item is in another item. Update the parent item id list and then move on. if ( item->GetParentID() && !item->GetLocInParent() ) { // Field missing. Needed for items in other items. Error2("Item with id %s has parent item without location in parent.\n",result[i]["id"]); delete item; continue; } loadeditems++; itemset->Add(item,parentid); } } } */ if (itemset->ResolveAllParents()) { for (size_t i=0; i < itemset->GetSize(); i++) if (itemset->Get(i)) itemset->Get(i)->SetLoaded(); return itemset; } else delete itemset; } return NULL; } bool CacheManager::PreloadItemStatsDatabase() { uint32 currentrow; psItemStats *newitem; Result result(db->Select("SELECT * from item_stats where stat_type='B'")); if (!result.IsValid()) return false; for (currentrow=0; currentrowReadItemStats(result[currentrow]); // Prevent name conflicts if (!loaded || itemStats_NameHash.Get(newitem->GetName(), NULL)) { if(loaded) CPrintf(CON_ERROR, "Duplicate item_stats name where name='%s' found.\n", newitem->GetName()); delete newitem; return false; } else { CS_ASSERT( newitem->GetUID() != 0 ); // basicitemstatslist.Push(newitem); itemStats_IDHash.Put(newitem->GetUID(),newitem); itemStats_NameHash.Put(newitem->GetName(),newitem); } } return true; } psItemStats* CacheManager::CopyItemStats( uint32 id ) { csString sql; sql.Append("INSERT INTO `item_stats` ( stat_type, name, weight, visible_distance, size, container_max_size, valid_slots, flags, decay_rate, \ item_skill_id_1, item_skill_id_2, item_skill_id_3, item_bonus_1_attr, item_bonus_2_attr, item_bonus_3_attr, item_bonus_1_max, \ item_bonus_2_max, item_bonus_3_max, weapon_speed, weapon_dmg_slash, weapon_dmg_blunt, weapon_dmg_pierce, weapon_dmg_force, \ weapon_dmg_fire, weapon_dmg_ice, weapon_dmg_air, weapon_dmg_poison, weapon_dmg_disease, weapon_dmg_holy, weapon_dmg_unholy, \ weapon_dmg_pct_slash, weapon_dmg_pct_blunt, weapon_dmg_pct_pierce, weapon_dmg_pct_force, weapon_dmg_pct_fire, weapon_dmg_pct_ice, \ weapon_dmg_pct_air, weapon_dmg_pct_poison, weapon_dmg_pct_disease, weapon_dmg_pct_holy, weapon_dmg_pct_unholy, weapon_penetration, \ weapon_block_targeted, weapon_block_untargeted, weapon_counterblock, armor_hardness, armor_prot_slash, \ armor_prot_blunt, armor_prot_pierce, armor_prot_force, armor_prot_fire, armor_prot_ice, armor_prot_air, \ armor_prot_poison, armor_prot_disease, armor_prot_holy, armor_prot_unholy, cstr_id_gfx_mesh, cstr_id_gfx_icon, \ cstr_id_gfx_texture, cstr_id_part, armorvsweapon_type, category_id, base_sale_price, item_type, \ requirement_1_name, requirement_1_value, requirement_2_name, requirement_2_value, requirement_3_name, \ requirement_3_value, item_type_id_ammo, spell_id_on_hit, spell_on_hit_prob, spell_id_feature, spell_feature_charges, \ spell_feature_timing, item_anim_id, description, sound, item_max_quality, prg_evt_equip, prg_evt_unequip,illum_definition) "); sql.Append("SELECT stat_type, name, weight, visible_distance, size, container_max_size, valid_slots, flags, decay_rate, item_skill_id_1, \ item_skill_id_2, item_skill_id_3, item_bonus_1_attr, item_bonus_2_attr, item_bonus_3_attr, item_bonus_1_max, item_bonus_2_max, \ item_bonus_3_max, weapon_speed, weapon_dmg_slash, weapon_dmg_blunt, weapon_dmg_pierce, weapon_dmg_force, weapon_dmg_fire, weapon_dmg_ice, \ weapon_dmg_air, weapon_dmg_poison, weapon_dmg_disease, weapon_dmg_holy, weapon_dmg_unholy, weapon_dmg_pct_slash, weapon_dmg_pct_blunt, \ weapon_dmg_pct_pierce, weapon_dmg_pct_force, weapon_dmg_pct_fire, weapon_dmg_pct_ice, weapon_dmg_pct_air, weapon_dmg_pct_poison, \ weapon_dmg_pct_disease, weapon_dmg_pct_holy, weapon_dmg_pct_unholy, weapon_penetration, weapon_block_targeted, weapon_block_untargeted, \ weapon_counterblock, armor_hardness, armor_prot_slash, armor_prot_blunt, armor_prot_pierce, armor_prot_force, armor_prot_fire, \ armor_prot_ice, armor_prot_air, armor_prot_poison, armor_prot_disease, armor_prot_holy, armor_prot_unholy, cstr_id_gfx_mesh, \ cstr_id_gfx_icon, cstr_id_gfx_texture, cstr_id_part, armorvsweapon_type, category_id, base_sale_price, item_type, requirement_1_name, \ requirement_1_value, requirement_2_name, requirement_2_value, requirement_3_name, requirement_3_value, item_type_id_ammo, spell_id_on_hit, \ spell_on_hit_prob, spell_id_feature, spell_feature_charges, spell_feature_timing, item_anim_id, description, sound, item_max_quality, \ prg_evt_equip, prg_evt_unequip,illum_definition FROM item_stats WHERE id = '%u'" ); psItemStats *newItem; if ( db->Command( sql.GetData(), id ) == QUERY_FAILED) { Error2("Error while copying item stats for id %d.\n",id); return NULL; } id = db->GetLastInsertID(); CS_ASSERT(id != 0); Result result( db->Select( "SELECT * from item_stats where id='%u'", id ) ); if ( !result.IsValid() ) return NULL; if ( result.Count() != 1 ) return NULL; newItem = new psItemStats; if ( !newItem->ReadItemStats( result[0] ) ) { delete newItem; return NULL; } // basicitemstatslist.Push( newItem ); itemStats_IDHash.Put(newItem->GetUID(),newItem); itemStats_NameHash.Put(newItem->GetName(),newItem); return newItem; } bool CacheManager::PreloadBadNames() { unsigned int currentRow; csString* name = NULL; Result result(db->Select("SELECT * from bad_names")); if (!result.IsValid()) { Error1("Couldn't load bad names!"); return false; } for (currentRow=0; currentRowCommand("INSERT INTO bad_names ( `name` ) VALUES ('%s')",newname->GetDataSafe()); bad_names.Push(newname); } void CacheManager::DelBadName(const char* name) { csString cname = NormalizeCharacterName(name); for(size_t i = 0; i < bad_names.Length();i++) { if(cname.CompareNoCase(*bad_names[i])) { bad_names.DeleteIndex(i); db->Command("DELETE FROM bad_names WHERE name='%s'",cname.GetData()); return; } } } size_t CacheManager::GetBadNamesCount() { return bad_names.Length(); } const char* CacheManager::GetBadName(int pos) { csString* string = bad_names.Get(pos); return string->GetData(); } psAccountInfo *CacheManager::GetAccountInfoByID(unsigned int accountid) { Result result(db->Select("SELECT * from accounts where id=%u",accountid)); if (!result.IsValid() || result.Count()<1) { Warning3(LOG_CONNECTIONS,"Could not find account for id %u. Error: %s",accountid,db->GetLastError()); return NULL; } psAccountInfo *accountinfo=new psAccountInfo; if (accountinfo->Load(result[0])) return accountinfo; else { delete accountinfo; return NULL; } } psAccountInfo *CacheManager::GetAccountInfoByCharID(unsigned int charid) { unsigned int accountid = db->SelectSingleNumber("SELECT account_id from characters where id=%u",charid); return GetAccountInfoByID( accountid ); } psAccountInfo *CacheManager::GetAccountInfoByUsername(const char *username) { iCachedObject *obj = RemoveFromCache(username); if (obj) { Notify2(LOG_CACHE, "Found account for %s in cache!", username); return (psAccountInfo *)obj->RecoverObject(); } csString escape; db->Escape( escape, username ); Result result(db->Select("SELECT * from accounts where username='%s'",escape.GetData())); if (!result.IsValid() || result.Count()<1) { Warning3(LOG_CONNECTIONS,"Could not find account for login %s. Error: %s",username,db->GetLastError()); return NULL; } psAccountInfo *accountinfo=new psAccountInfo; if (accountinfo->Load(result[0])) return accountinfo; else { delete accountinfo; return NULL; } } bool CacheManager::UpdateAccountInfo(psAccountInfo *ainfo) { const char *fieldnames[]= { "username", "password", "last_login_ip", "security_level", "last_login" }; char accountidstring[11]; psStringArray fields; fields.Push(ainfo->username); fields.Push(ainfo->password); fields.Push(ainfo->lastloginip); fields.FormatPush("%d",ainfo->securitylevel); fields.Push(ainfo->lastlogintime); snprintf(accountidstring,11,"%u",ainfo->accountid); accountidstring[10]=0x00; if (!db->GenericUpdateWithID("accounts","id",accountidstring,fieldnames,fields)) { Error3("Failed to update account %u. Error %s",ainfo->accountid,db->GetLastError()); return false; } return true; } unsigned int CacheManager::NewAccountInfo(psAccountInfo *ainfo) { const char *fieldnames[]= { "username", "password", "last_login_ip", "security_level" }; psStringArray fields; fields.Push(ainfo->username); fields.Push(ainfo->password); fields.Push(ainfo->lastloginip); fields.FormatPush("%d",ainfo->securitylevel); unsigned int id=db->GenericInsertWithID("accounts",fieldnames,fields); if (id == 0) { Error3("Failed to create new account for user %s. Error %s",ainfo->username.GetData(),db->GetLastError()); return false; } ainfo->accountid = id; return id; } psGuildInfo *CacheManager::FindGuild(unsigned int id) { psGuildInfo *g = guildinfo_by_id.Get(id, 0); if (g) return g; // Load on demand if not found g = new psGuildInfo; if (g->Load(id)) { guildinfo_by_id.Put(g->id,g); return g; } delete g; return NULL; } psGuildInfo * CacheManager::FindGuild(const csString & name) { unsigned int id; csString escape; db->Escape( escape, name ); Result result(db->Select("select id from guilds where name=\"%s\"", escape.GetData()) ); if (!result.IsValid()) return NULL; if (result.Count() == 0) return NULL; id = result[0].GetInt("id"); return FindGuild(id); } bool CacheManager::CreateGuild(const char *guildname, Client *client) { int leaderID = client->GetPlayerID(); psGuildInfo *gi = new psGuildInfo(guildname,leaderID); if (!gi->InsertNew(leaderID)) { delete gi; return false; } gi->AddNewMember(client->GetCharacterData(), MAX_GUILD_LEVEL); guildinfo_by_id.Put(gi->id,gi); return true; } void CacheManager::RemoveGuild(psGuildInfo *which) { guildinfo_by_id.Delete(which->id,which); delete which; } psGuildAlliance * CacheManager::FindAlliance(unsigned int id) { psGuildAlliance *a = alliance_by_id.Get(id, 0); if (a) return a; a = new psGuildAlliance(); if (a->Load(id)) { alliance_by_id.Put(id,a); return a; } delete a; return NULL; } bool CacheManager::CreateAlliance(const csString & name, psGuildInfo * founder, Client *client) { psGuildAlliance *a = new psGuildAlliance(name); if (!a->InsertNew()) { delete a; return false; } a->AddNewMember(founder); a->SetLeader(founder); alliance_by_id.Put(a->GetID(),a); return true; } bool CacheManager::RemoveAlliance(psGuildAlliance *which) { alliance_by_id.Delete(which->GetID(),which); which->RemoveAlliance(); delete which; return true; } float CacheManager::GetArmorVSWeaponResistance(const char* armor_type, const char* weapon_type) { if(!weapon_type || !armor_type || strlen(armor_type) < 2) return 1.0f; // Loopide loop for(size_t i = 0;i < armor_vs_weapon.Length();i++) { ArmorVsWeapon* atbl = armor_vs_weapon.Get(i); if(!atbl) continue; if(atbl->weapontype == weapon_type) { // Get the float in our 2d array csString armor = armor_type; char lvl = armor.GetAt(1); int arrayLvl = 0; int cat = atoi(csString(armor.GetAt(0))); if(lvl == 'a') arrayLvl = 0; else if(lvl =='b') arrayLvl = 1; else if(lvl == 'c') arrayLvl = 2; else arrayLvl = 3; return atbl->c[cat-1][arrayLvl]; } } // Not found Debug2(LOG_COMBAT,0,"Didn't find weapon %s in the armor vs weapon table, assuming value is 1.0\n",weapon_type); return 1.0f; } float CacheManager::GetArmorVSWeaponResistance(psItemStats* armor, psItemStats* weapon) { if(!armor || !weapon) return 1.0f; // Get the values to use csString armorstr,weaponstr; armor->GetArmorVsWeaponType(armorstr); weapon->GetArmorVsWeaponType(weaponstr); return GetArmorVSWeaponResistance(armorstr,weaponstr); } void CacheManager::CacheCraftTransforms( psMsgCraftingInfo* tradePattern, int patternID, int groupID ) { /// Select all the items that are used in the transformations. Result transItems(db->Select("SELECT * FROM trade_transformations where pattern_id=%d OR pattern_id=%d", patternID, groupID)); for ( int currentTrans = 0; currentTrans < (int)transItems.Count(); currentTrans++ ) { // Don't load the garbage trasfromations for the message int reqItem = transItems[currentTrans].GetInt("item_id"); if( reqItem != 0) { psMsgCraftingInfo::CraftingItem* trans = new psMsgCraftingInfo::CraftingItem; int resultItem = transItems[currentTrans].GetInt("result_id"); Result equipName(db->Select("SELECT name from item_stats where id=%d", transItems[currentTrans].GetInt("equipment_id"))); if ( equipName.IsValid() && equipName.Count() > 0 ) { trans->requiredEquipment = equipName[0]["name"]; } Result workItemName(db->Select("SELECT name from item_stats where id=%d", transItems[currentTrans].GetInt("workitem_id"))); if ( workItemName.IsValid() && workItemName.Count() > 0 ) { trans->requiredWorkItem = workItemName[0]["name"]; } Result itemName(db->Select("SELECT name from item_stats where id=%d", resultItem) ); if ( itemName.IsValid() && itemName.Count() > 0 ) { csString name = itemName[0]["name"]; trans->name = name; trans->count = 0; psMsgCraftingInfo::CraftingItem* subtrans = new psMsgCraftingInfo::CraftingItem; Result reqItemName(db->Select("SELECT name from item_stats where id=%d", reqItem) ); if ( reqItemName.IsValid() && reqItemName.Count()>0 ) { name = reqItemName[0]["name"]; subtrans->name = name; subtrans->count = transItems[currentTrans].GetInt("item_qty"); trans->subItems.Push( subtrans ); tradePattern->items.Push( trans ); } else { Error2("CacheManager::CacheCraftTransforms - Failed to load item_stats for item %d.",reqItem); delete trans; } } else { delete trans; } } } } void CacheManager::CacheCraftCombos( psMsgCraftingInfo* tradePattern, int patternID, int groupID ) { /// Select all the items that are used in the combination of this item. ToDo: ordering the results by item_id will speedup/simplify match Result combItems(db->Select("SELECT distinct result_id FROM trade_combinations where pattern_id=%d OR pattern_id=%d", patternID, groupID)); if (combItems.IsValid()) { for ( int currentCombo = 0; currentCombo < (int)combItems.Count(); currentCombo++ ) { uint32 resultItem = combItems[currentCombo].GetUInt32("result_id"); Result itemName(db->Select("SELECT name from item_stats where id=%d", resultItem) ); if ( itemName.IsValid()) { if (itemName.Count()==0) { Error2("CacheManager::CacheCraftCombos - Failed to load item_stats for item %d.",resultItem); continue; } csString name = itemName[0]["name"]; psMsgCraftingInfo::CraftingItem* comb = new psMsgCraftingInfo::CraftingItem; comb->name = name; comb->count = 0; /// Get all the items involved in creating this item. Result requiredItems(db->Select("SELECT * FROM `trade_combinations` where result_id=%d", resultItem)); if ( !requiredItems.IsValid() ) { Error2("CacheManager::CacheCraftCombinations - Failed to load trade_combinations for item %d.",resultItem); delete comb; return; } for ( int currentReqItem = 0; currentReqItem < (int)requiredItems.Count(); currentReqItem++ ) { psMsgCraftingInfo::CraftingItem* reqItem = new psMsgCraftingInfo::CraftingItem; uint32 reqID = requiredItems[currentReqItem].GetUInt32("item_id"); Result itemName(db->Select("SELECT name from item_stats where id=%d", reqID) ); if ( !itemName.IsValid() || itemName.Count() == 0) { Error2("CacheManager::CacheCraftCombinations - Failed to load item_stats for item %d.",reqID); delete comb; delete reqItem; return; } csString name = itemName[0]["name"]; reqItem->name = name; reqItem->count = requiredItems[currentReqItem].GetInt("min_qty"); comb->subItems.Push( reqItem ); } tradePattern->items.Push( comb ); } } } } void CacheManager::PreloadUpdateInfo() { csRef vfs = CS_QUERY_REGISTRY (psserver->GetObjectReg(), iVFS); CS_ASSERT(vfs != NULL); csRef data = vfs->ReadFile("/this/version.dat"); if (!data) { Warning1(LOG_ANY,"Missing /this/version.dat! Cannot send update notifications to clients."); updateTimeStamp = 0; return; } // The first 32 characters are the version stamp. // We want the time stamp after it. csString tmp(data->GetData()); tmp.DeleteAt(0,33); updateTimeStamp = atoi( tmp.GetDataSafe() ); } const char *CacheManager::MakeCacheName(const char *prefix, uint32 id) { sprintf(CacheNameBuffer,"%.5s%u",prefix,id); return CacheNameBuffer; } void CacheManager::AddToCache(iCachedObject *obj, const char *name, int max_cache_time_seconds) { Debug4(LOG_CACHE,0,"Now adding object <%s:%p> to cache for %d seconds.",name,obj,max_cache_time_seconds); // Set up the object which the event trigger will use to recover pointers or cancel itself. CachedObject *newRecord = new CachedObject; newRecord->name = name; newRecord->object = obj; // Now create the timed event which will make the cache clean it up later psCacheExpireEvent *evt = new psCacheExpireEvent(max_cache_time_seconds*1000, newRecord); // Setting this enables removal from cache to clear this event newRecord->event = evt; // Now queue it psserver->GetEventManager()->Push(evt); // Ensure no duplicate keys in generic cache, and clean up after self if found iCachedObject *oldObject = RemoveFromCache(newRecord->name); if (oldObject) { oldObject->ProcessCacheTimeout(); // Make sure object knows it is gone oldObject->DeleteSelf(); } // Now add the new one back in generic_object_cache.Put(newRecord->name, newRecord); } iCachedObject *CacheManager::RemoveFromCache(const char *name) { CachedObject *oldRecord = generic_object_cache.Get(name, NULL); if (oldRecord) { oldRecord->event->CancelEvent(); generic_object_cache.DeleteAll(oldRecord->name); iCachedObject *save = oldRecord->object; delete oldRecord; Notify2(LOG_CACHE,"Found object in cache and returning ptr %p.",save); return save; } Debug2(LOG_CACHE,0,"Object <%s> not found in cache.",name); return NULL; } CacheManager::psCacheExpireEvent::psCacheExpireEvent(int delayticks,CachedObject *object) : psGameEvent(0,delayticks,"psCacheExpireEvent") { valid = true; myObject = object; } void CacheManager::psCacheExpireEvent::Trigger() { if (valid) { Debug2(LOG_CACHE,0,"Deleting object <%s> from cache due to timeout.",myObject->name.GetDataSafe()); // Notify object it is going away myObject->object->ProcessCacheTimeout(); // Delete the underlying object myObject->object->DeleteSelf(); // Now remove the record from the cache last CacheManager::GetSingleton().RemoveFromCache(myObject->name); } }