#include "shareddb.h" #include #include #include #include "Item.h" #include "classes.h" #include "rulesys.h" #include "seperator.h" #include "MiscFunctions.h" #include "eq_packet_structs.h" #include "guilds.h" #include "extprofile.h" #include "memory_mapped_file.h" #include "ipc_mutex.h" #include "eqemu_exception.h" #include "loottable.h" #include "faction.h" #include "features.h" using namespace std; SharedDatabase::SharedDatabase() : Database(), skill_caps_mmf(NULL), items_mmf(NULL), items_hash(NULL), faction_mmf(NULL), faction_hash(NULL), loot_table_mmf(NULL), loot_drop_mmf(NULL), loot_table_hash(NULL), loot_drop_hash(NULL) { } SharedDatabase::SharedDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) : Database(host, user, passwd, database, port), skill_caps_mmf(NULL), items_mmf(NULL), items_hash(NULL), faction_mmf(NULL), faction_hash(NULL), loot_table_mmf(NULL), loot_drop_mmf(NULL), loot_table_hash(NULL), loot_drop_hash(NULL) { } SharedDatabase::~SharedDatabase() { safe_delete(skill_caps_mmf); safe_delete(items_mmf); safe_delete(items_hash); safe_delete(faction_mmf); safe_delete(faction_hash); safe_delete(loot_table_mmf); safe_delete(loot_drop_mmf); safe_delete(loot_table_hash); safe_delete(loot_drop_hash); } bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET hideme = %i where id = %i", hideme, account_id), errbuf)) { cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } uint8 SharedDatabase::GetGMSpeed(uint32 account_id) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT gmspeed FROM account where id='%i'", account_id), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); uint8 gmspeed = atoi(row[0]); mysql_free_result(result); return gmspeed; } else { mysql_free_result(result); return 0; } mysql_free_result(result); } else { cerr << "Error in GetGMSpeed query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } return 0; } bool SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET gmspeed = %i where id = %i", gmspeed, account_id), errbuf)) { cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { uint32 EntitledTime = 0; const char *EntitledQuery = "select sum(ascii(substring(profile, 237, 1)) + (ascii(substring(profile, 238, 1)) * 256) +" "(ascii(substring(profile, 239, 1)) * 65536) + (ascii(substring(profile, 240, 1)) * 16777216))" "from character_ where account_id = %i"; char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, EntitledQuery, AccountID), errbuf, &result)) { if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); EntitledTime = atoi(row[0]); } mysql_free_result(result); } safe_delete_array(query); return EntitledTime; } bool SharedDatabase::SaveCursor(uint32 char_id, list::const_iterator &start, list::const_iterator &end) { iter_queue it; int i; bool ret=true; char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; // Delete cursor items if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND ( (slotid >=8000 and slotid<=8999) or slotid=30 or (slotid>=331 and slotid<=340))", char_id), errbuf))) { for(it=start,i=8000;it!=end;it++,i++) { ItemInst *inst=*it; if (!(ret=SaveInventory(char_id,inst,(i==8000) ? 30 : i))) break; } } else { cout << "Clearing cursor failed: " << errbuf << endl; } safe_delete_array(query); return ret; } bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES *result; MYSQL_ROW row; // Delete cursor items if (!RunQuery(query, MakeAnyLenString(&query, "SELECT itemid,charges FROM sharedbank " "WHERE acctid=%d AND slotid=%d", account_id, slot_id), errbuf, &result)) { LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query, errbuf); safe_delete_array(query); //returning true is less harmful in the face of a query error return(true); } safe_delete_array(query); row = mysql_fetch_row(result); bool found = false; if(row) { uint32 id = atoi(row[0]); uint16 charges = atoi(row[1]); uint16 expect_charges = 0; if(inst->GetCharges() >= 0) expect_charges = inst->GetCharges(); else expect_charges = 0x7FFF; if(id == inst->GetItem()->ID && charges == expect_charges) found = true; } mysql_free_result(result); return(found); } bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { _CP(Database_SaveInventory); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; bool ret = false; uint32 augslot[5] = { 0, 0, 0, 0, 0 }; //never save tribute slots: if(slot_id >= 400 && slot_id <= 404) return(true); if (inst && inst->IsType(ItemClassCommon)) { for(int i=0;i<5;i++) { ItemInst *auginst=inst->GetItem(i); augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; } } if (slot_id>=2500 && slot_id<=2600) { // Shared bank inventory if (!inst) { // Delete item uint32 account_id = GetAccountIDByChar(char_id); uint32 len_query = MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id); ret = RunQuery(query, len_query, errbuf); // Delete bag slots, if need be if (ret && Inventory::SupportsContainers(slot_id)) { safe_delete_array(query); int16 base_slot_id = Inventory::CalcSlotId(slot_id, 0); ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid>=%i AND slotid<%i", account_id, base_slot_id, (base_slot_id+10)), errbuf); } // @merth: need to delete augments here } else { // Update/Insert item uint32 account_id = GetAccountIDByChar(char_id); uint16 charges = 0; if(inst->GetCharges() >= 0) charges = inst->GetCharges(); else charges = 0x7FFF; uint32 len_query = MakeAnyLenString(&query, "REPLACE INTO sharedbank " " (acctid,slotid,itemid,charges,custom_data," " augslot1,augslot2,augslot3,augslot4,augslot5)" " VALUES(%lu,%lu,%lu,%lu,'%s'," " %lu,%lu,%lu,%lu,%lu)", (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, inst->GetCustomDataString().c_str(), (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); ret = RunQuery(query, len_query, errbuf); } } else { // All other inventory if (!inst) { // Delete item ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid=%i", char_id, slot_id), errbuf); // Delete bag slots, if need be if (ret && Inventory::SupportsContainers(slot_id)) { safe_delete_array(query); int16 base_slot_id = Inventory::CalcSlotId(slot_id, 0); ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid>=%i AND slotid<%i", char_id, base_slot_id, (base_slot_id+10)), errbuf); } // @merth: need to delete augments here } else { uint16 charges = 0; if(inst->GetCharges() >= 0) charges = inst->GetCharges(); else charges = 0x7FFF; // Update/Insert item uint32 len_query = MakeAnyLenString(&query, "REPLACE INTO inventory " " (charid,slotid,itemid,charges,instnodrop,custom_data,color," " augslot1,augslot2,augslot3,augslot4,augslot5)" " VALUES(%lu,%lu,%lu,%lu,%lu,'%s',%lu," " %lu,%lu,%lu,%lu,%lu)", (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, (unsigned long)(inst->IsInstNoDrop() ? 1:0),inst->GetCustomDataString().c_str(),(unsigned long)inst->GetColor(), (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4] ); ret = RunQuery(query, len_query, errbuf); } } if (!ret) LogFile->write(EQEMuLog::Error, "SaveInventory query '%s': %s", query, errbuf); safe_delete_array(query); // Save bag contents, if slot supports bag contents if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { for (uint8 idx=0; idx<10; idx++) { const ItemInst* baginst = inst->GetItem(idx); SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); } } // @merth: need to save augments here return ret; } int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT sharedplat FROM account WHERE id='%i'", account_id), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); uint32 shared_platinum = atoi(row[0]); mysql_free_result(result); return shared_platinum; } else { mysql_free_result(result); return 0; } mysql_free_result(result); } else { cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } return 0; } bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } safe_delete_array(query); return true; } bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES *result; MYSQL_ROW row; const Item_Struct* myitem; RunQuery ( query, MakeAnyLenString ( &query, "SELECT itemid, item_charges, slot FROM starting_items " "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " "(deityid = %i or deityid=0) AND (zoneid = %i or zoneid = 0) AND " "gm <= %i ORDER BY id", si_race, si_class, si_deity, si_current_zone, admin_level ), errbuf, &result ); safe_delete_array(query); while((row = mysql_fetch_row(result))) { int itemid = atoi(row[0]); int charges = atoi(row[1]); int slot = atoi(row[2]); myitem = GetItem(itemid); if(!myitem) continue; ItemInst* myinst = CreateBaseItem(myitem, charges); if(slot < 0) slot = inv->FindFreeSlot(0,0); inv->PutItem(slot, *myinst); safe_delete(myinst); } if(result) mysql_free_result(result); return true; } // Retrieve shared bank inventory based on either account or character bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 len_query = 0; MYSQL_RES *result; MYSQL_ROW row; bool ret = false; if (is_charid) { len_query = MakeAnyLenString(&query, "SELECT sb.slotid,sb.itemid,sb.charges,sb.augslot1,sb.augslot2,sb.augslot3,sb.augslot4,sb.augslot5,sb.custom_data from sharedbank sb " "INNER JOIN character_ ch ON ch.account_id=sb.acctid " "WHERE ch.id=%i", id); } else { len_query = MakeAnyLenString(&query, "SELECT slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,custom_data from sharedbank WHERE acctid=%i", id); } if (RunQuery(query, len_query, errbuf, &result)) { while ((row = mysql_fetch_row(result))) { int16 slot_id = (int16)atoi(row[0]); uint32 item_id = (uint32)atoi(row[1]); int8 charges = (int8)atoi(row[2]); uint32 aug[5]; aug[0] = (uint32)atoi(row[3]); aug[1] = (uint32)atoi(row[4]); aug[2] = (uint32)atoi(row[5]); aug[3] = (uint32)atoi(row[6]); aug[4] = (uint32)atoi(row[7]); const Item_Struct* item = GetItem(item_id); if (item) { int16 put_slot_id = SLOT_INVALID; ItemInst* inst = CreateBaseItem(item, charges); if (item->ItemClass == ItemClassCommon) { for(int i=0;i<5;i++) { if (aug[i]) { inst->PutAugment(this, i, aug[i]); } } } if(row[8]) { std::string data_str(row[8]); std::string id; std::string value; bool use_id = true; for(int i = 0; i < data_str.length(); ++i) { if(data_str[i] == '^') { if(!use_id) { inst->SetCustomData(id, value); id.clear(); value.clear(); } use_id = !use_id; } else { char v = data_str[i]; if(use_id) { id.push_back(v); } else { value.push_back(v); } } } } put_slot_id = inv->PutItem(slot_id, *inst); safe_delete(inst); // Save ptr to item in inventory if (put_slot_id == SLOT_INVALID) { LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); if(is_charid) SaveInventory(id,NULL,slot_id); } } else { LogFile->write(EQEMuLog::Error, "Warning: %s %i has an invalid item_id %i in inventory slot %i", ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); } } mysql_free_result(result); ret = true; } else { LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); } safe_delete_array(query); return ret; } // Overloaded: Retrieve character inventory based on character id bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { _CP(Database_GetInventory); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES* result; MYSQL_ROW row; bool ret = false; // Retrieve character inventory if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," "instnodrop,custom_data FROM inventory WHERE charid=%i ORDER BY slotid", char_id), errbuf, &result)) { while ((row = mysql_fetch_row(result))) { int16 slot_id = atoi(row[0]); uint32 item_id = atoi(row[1]); uint16 charges = atoi(row[2]); uint32 color = atoul(row[3]); uint32 aug[5]; aug[0] = (uint32)atoul(row[4]); aug[1] = (uint32)atoul(row[5]); aug[2] = (uint32)atoul(row[6]); aug[3] = (uint32)atoul(row[7]); aug[4] = (uint32)atoul(row[8]); bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; const Item_Struct* item = GetItem(item_id); if (item) { int16 put_slot_id = SLOT_INVALID; ItemInst* inst = CreateBaseItem(item, charges); if(row[10]) { std::string data_str(row[10]); std::string id; std::string value; bool use_id = true; for(int i = 0; i < data_str.length(); ++i) { if(data_str[i] == '^') { if(!use_id) { inst->SetCustomData(id, value); id.clear(); value.clear(); } use_id = !use_id; } else { char v = data_str[i]; if(use_id) { id.push_back(v); } else { value.push_back(v); } } } } if (instnodrop || (slot_id >= 0 && slot_id <= 21 && inst->GetItem()->Attuneable)) inst->SetInstNoDrop(true); if (color > 0) inst->SetColor(color); if(charges==0x7FFF) inst->SetCharges(-1); else inst->SetCharges(charges); if (item->ItemClass == ItemClassCommon) { for(int i=0;i<5;i++) { if (aug[i]) { inst->PutAugment(this, i, aug[i]); } } } if (slot_id>=8000 && slot_id <= 8999) put_slot_id = inv->PushCursor(*inst); else put_slot_id = inv->PutItem(slot_id, *inst); safe_delete(inst); // Save ptr to item in inventory if (put_slot_id == SLOT_INVALID) { LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", char_id, item_id, slot_id); } } else { LogFile->write(EQEMuLog::Error, "Warning: charid %i has an invalid item_id %i in inventory slot %i", char_id, item_id, slot_id); } } mysql_free_result(result); // Retrieve shared inventory ret = GetSharedBank(char_id, inv, true); } else { LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); } safe_delete_array(query); return ret; } // Overloaded: Retrieve character inventory based on account_id and character name bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) { _CP(Database_GetInventory_name); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES* result; MYSQL_ROW row; bool ret = false; // Retrieve character inventory if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," "instnodrop,custom_data FROM inventory INNER JOIN character_ ch ON ch.id=charid WHERE ch.name='%s' AND ch.account_id=%i ORDER BY slotid", name, account_id), errbuf, &result)) { while ((row = mysql_fetch_row(result))) { int16 slot_id = atoi(row[0]); uint32 item_id = atoi(row[1]); int8 charges = atoi(row[2]); uint32 color = atoul(row[3]); uint32 aug[5]; aug[0] = (uint32)atoi(row[4]); aug[1] = (uint32)atoi(row[5]); aug[2] = (uint32)atoi(row[6]); aug[3] = (uint32)atoi(row[7]); aug[4] = (uint32)atoi(row[8]); bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; const Item_Struct* item = GetItem(item_id); int16 put_slot_id = SLOT_INVALID; if(!item) continue; ItemInst* inst = CreateBaseItem(item, charges); inst->SetInstNoDrop(instnodrop); if(row[10]) { std::string data_str(row[10]); std::string id; std::string value; bool use_id = true; for(int i = 0; i < data_str.length(); ++i) { if(data_str[i] == '^') { if(!use_id) { inst->SetCustomData(id, value); id.clear(); value.clear(); } use_id = !use_id; } else { char v = data_str[i]; if(use_id) { id.push_back(v); } else { value.push_back(v); } } } } if (color > 0) inst->SetColor(color); inst->SetCharges(charges); if (item->ItemClass == ItemClassCommon) { for(int i=0;i<5;i++) { if (aug[i]) { inst->PutAugment(this, i, aug[i]); } } } if (slot_id>=8000 && slot_id <= 8999) put_slot_id = inv->PushCursor(*inst); else put_slot_id = inv->PutItem(slot_id, *inst); safe_delete(inst); // Save ptr to item in inventory if (put_slot_id == SLOT_INVALID) { LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", name, account_id, item_id, slot_id); } } mysql_free_result(result); // Retrieve shared inventory ret = GetSharedBank(account_id, inv, false); } else { LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); } safe_delete_array(query); return ret; } void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; item_count = -1; max_id = 0; char query[] = "SELECT MAX(id), count(*) FROM items"; if (RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { row = mysql_fetch_row(result); if (row != NULL && row[1] != 0) { item_count = atoi(row[1]); if(row[0]) max_id = atoi(row[0]); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query, errbuf); } } bool SharedDatabase::LoadItems() { if(items_mmf) { return true; } try { EQEmu::IPCMutex mutex("items"); mutex.Lock(); items_mmf = new EQEmu::MemoryMappedFile("shared/items"); int32 items = -1; uint32 max_item = 0; GetItemsCount(items, max_item); if(items == -1) { EQ_EXCEPT("SharedDatabase", "Database returned no result"); } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size(items, max_item)); if(items_mmf->Size() != size) { EQ_EXCEPT("SharedDatabase", "Couldn't load items because items_mmf->Size() != size"); } items_hash = new EQEmu::FixedMemoryHashSet(reinterpret_cast(items_mmf->Get()), size); mutex.Unlock(); } catch(std::exception& ex) { LogFile->write(EQEMuLog::Error, "Error Loading Items: %s", ex.what()); return false; } return true; } void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id); char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; char ndbuffer[4]; bool disableNoRent = false; if(GetVariable("disablenorent", ndbuffer, 4)) { if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { disableNoRent = true; } } bool disableNoDrop = false; if(GetVariable("disablenodrop", ndbuffer, 4)) { if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { disableNoDrop = true; } } bool disableLoreGroup = false; if(GetVariable("disablelore", ndbuffer, 4)) { if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { disableLoreGroup = true; } } bool disableNoTransfer = false; if(GetVariable("disablenotransfer", ndbuffer, 4)) { if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { disableNoTransfer = true; } } char query[] = "select source," #define F(x) "`"#x"`," #include "item_fieldlist.h" #undef F "updated" " from items order by id"; Item_Struct item; if(RunQuery(query, sizeof(query), errbuf, &result)) { while((row = mysql_fetch_row(result))) { memset(&item, 0, sizeof(Item_Struct)); item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); strcpy(item.Name,row[ItemField::name]); strcpy(item.Lore,row[ItemField::lore]); strcpy(item.IDFile,row[ItemField::idfile]); item.ID = (uint32)atoul(row[ItemField::id]); item.Weight = (uint8)atoi(row[ItemField::weight]); item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); item.Size = (uint8)atoi(row[ItemField::size]); item.Slots = (uint32)atoul(row[ItemField::slots]); item.Price = (uint32)atoul(row[ItemField::price]); item.Icon = (uint32)atoul(row[ItemField::icon]); item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; item.CR = (int8)atoi(row[ItemField::cr]); item.DR = (int8)atoi(row[ItemField::dr]); item.PR = (int8)atoi(row[ItemField::pr]); item.MR = (int8)atoi(row[ItemField::mr]); item.FR = (int8)atoi(row[ItemField::fr]); item.AStr = (int8)atoi(row[ItemField::astr]); item.ASta = (int8)atoi(row[ItemField::asta]); item.AAgi = (int8)atoi(row[ItemField::aagi]); item.ADex = (int8)atoi(row[ItemField::adex]); item.ACha = (int8)atoi(row[ItemField::acha]); item.AInt = (int8)atoi(row[ItemField::aint]); item.AWis = (int8)atoi(row[ItemField::awis]); item.HP = (int32)atoul(row[ItemField::hp]); item.Mana = (int32)atoul(row[ItemField::mana]); item.AC = (int32)atoul(row[ItemField::ac]); item.Deity = (uint32)atoul(row[ItemField::deity]); item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); //item.Unk033 = (int32)atoul(row[ItemField::UNK033]); item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); item.BardType = (uint32)atoul(row[ItemField::bardtype]); item.BardValue = (int32)atoul(row[ItemField::bardvalue]); item.Light = (int8)atoi(row[ItemField::light]); item.Delay = (uint8)atoi(row[ItemField::delay]); item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); item.RecSkill = (uint8)atoi(row[ItemField::recskill]); item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); item.Range = (uint8)atoi(row[ItemField::range]); item.Damage = (uint32)atoi(row[ItemField::damage]); item.Color = (uint32)atoul(row[ItemField::color]); item.Classes = (uint32)atoul(row[ItemField::classes]); item.Races = (uint32)atoul(row[ItemField::races]); //item.Unk054 = (uint32)atoul(row[ItemField::UNK054]); item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); item.ItemType = (uint8)atoi(row[ItemField::itemtype]); item.Material = (uint8)atoi(row[ItemField::material]); item.SellRate = (float)atof(row[ItemField::sellrate]); //item.Unk059 = (uint32)atoul(row[ItemField::UNK059]); item.CastTime = (uint32)atoul(row[ItemField::casttime]); item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); item.ProcRate = (int32)atoi(row[ItemField::procrate]); item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); item.Shielding = (int8)atoi(row[ItemField::shielding]); item.StunResist = (int8)atoi(row[ItemField::stunresist]); item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); item.SpellShield = (int8)atoi(row[ItemField::spellshield]); item.Avoidance = (int8)atoi(row[ItemField::avoidance]); item.Accuracy = (int8)atoi(row[ItemField::accuracy]); item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); strcpy(item.CharmFile,row[ItemField::charmfile]); item.AugType = (uint32)atoul(row[ItemField::augtype]); item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); item.AugSlotUnk2[0] = 0; item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); item.AugSlotUnk2[1] = 0; item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); item.AugSlotUnk2[2] = 0; item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); item.AugSlotUnk2[3] = 0; item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); item.AugSlotUnk2[4] = 0; item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); item.BagType = (uint8)atoi(row[ItemField::bagtype]); item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); item.BagSize = (uint8)atoi(row[ItemField::bagsize]); item.BagWR = (uint8)atoi(row[ItemField::bagwr]); item.Book = (uint8)atoi(row[ItemField::book]); item.BookType = (uint32)atoul(row[ItemField::booktype]); strcpy(item.Filename,row[ItemField::filename]); item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); item.LoreFlag = item.LoreGroup!=0; item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; item.Favor = (uint32)atoul(row[ItemField::favor]); item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; item.Endur = (uint32)atoul(row[ItemField::endur]); item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); item.Attack = (uint32)atoul(row[ItemField::attack]); item.Regen = (uint32)atoul(row[ItemField::regen]); item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); item.Haste = (uint32)atoul(row[ItemField::haste]); item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); item.RecastType = (uint32)atoul(row[ItemField::recasttype]); item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; item.PointType = (uint32)atoul(row[ItemField::pointtype]); item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; item.StackSize = (uint16)atoi(row[ItemField::stacksize]); item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); strcpy(item.CharmFile,row[ItemField::charmfile]); item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); item.Purity = (uint32)atoul(row[ItemField::purity]); item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); item.HealAmt = (int32)atoi(row[ItemField::healamt]); item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); strcpy(item.ClickName,row[ItemField::clickname]); strcpy(item.ProcName,row[ItemField::procname]); strcpy(item.WornName,row[ItemField::wornname]); strcpy(item.FocusName,row[ItemField::focusname]); strcpy(item.ScrollName,row[ItemField::scrollname]); try { hash.insert(item.ID, item); } catch(std::exception &ex) { LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); break; } } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query, errbuf); } } const Item_Struct* SharedDatabase::GetItem(uint32 id) { if(!items_hash || id > items_hash->max_key()) { return NULL; } if(items_hash->exists(id)) { return &(items_hash->at(id)); } return NULL; } const Item_Struct* SharedDatabase::IterateItems(uint32* id) { if(!items_hash || !id) { return NULL; } for(;;) { if(*id > items_hash->max_key()) { break; } if(items_hash->exists(*id)) { return &(items_hash->at((*id)++)); } else { ++(*id); } } return NULL; } string SharedDatabase::GetBook(const char *txtfile) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; char txtfile2[20]; string txtout; strcpy(txtfile2,txtfile); if (!RunQuery(query, MakeAnyLenString(&query, "SELECT txtfile FROM books where name='%s'", txtfile2), errbuf, &result)) { cerr << "Error in GetBook query '" << query << "' " << errbuf << endl; if (query != 0) safe_delete_array(query); txtout.assign(" ",1); return txtout; } else { safe_delete_array(query); if (mysql_num_rows(result) == 0) { mysql_free_result(result); LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); txtout.assign(" ",1); return txtout; } else { row = mysql_fetch_row(result); txtout.assign(row[0],strlen(row[0])); mysql_free_result(result); return txtout; } } } void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { list_count = 0; max_lists = 0; const char *query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query, strlen(query), errbuf, &result)) { if(row = mysql_fetch_row(result)) { list_count = static_cast(atoul(row[0])); max_lists = static_cast(atoul(row[1])); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); } } const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { if(!faction_hash) { return NULL; } if(faction_hash->exists(id)) { return &(faction_hash->at(id)); } return NULL; } void SharedDatabase::LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, list_count, max_lists); const char *query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, npc_faction_entries.temp " "FROM npc_faction LEFT JOIN npc_faction_entries ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY " "npc_faction.id;"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; NPCFactionList faction; if(RunQuery(query, strlen(query), errbuf, &result)) { uint32 current_id = 0; uint32 current_entry = 0; while(row = mysql_fetch_row(result)) { uint32 id = static_cast(atoul(row[0])); if(id != current_id) { if(current_id != 0) { hash.insert(current_id, faction); } memset(&faction, 0, sizeof(faction)); current_entry = 0; current_id = id; faction.id = id; faction.primaryfaction = static_cast(atoul(row[1])); faction.assistprimaryfaction = (atoi(row[2]) == 0); } if(!row[3]) { continue; } if(current_entry >= MAX_NPC_FACTIONS) { continue; } faction.factionid[current_entry] = static_cast(atoul(row[3])); faction.factionvalue[current_entry] = static_cast(atoi(row[4])); faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); faction.factiontemp[current_entry] = static_cast(atoi(row[6])); ++current_entry; } if(current_id != 0) { hash.insert(current_id, faction); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); } } bool SharedDatabase::LoadNPCFactionLists() { if(faction_hash) { return true; } try { EQEmu::IPCMutex mutex("faction"); mutex.Lock(); faction_mmf = new EQEmu::MemoryMappedFile("shared/faction"); uint32 list_count = 0; uint32 max_lists = 0; GetFactionListInfo(list_count, max_lists); if(list_count == 0) { EQ_EXCEPT("SharedDatabase", "Database returned no result"); } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size( list_count, max_lists)); if(faction_mmf->Size() != size) { EQ_EXCEPT("SharedDatabase", "Couldn't load npc factions because faction_mmf->Size() != size"); } faction_hash = new EQEmu::FixedMemoryHashSet(reinterpret_cast(faction_mmf->Get()), size); mutex.Unlock(); } catch(std::exception& ex) { LogFile->write(EQEMuLog::Error, "Error Loading npc factions: %s", ex.what()); return false; } return true; } // Get the player profile and inventory for the given account "account_id" and // character name "name". Return true if the character was found, otherwise false. // False will also be returned if there is a database error. bool SharedDatabase::GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone, uint32 *current_instance) { _CP(Database_GetPlayerProfile); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES* result; MYSQL_ROW row; bool ret = false; unsigned long* lengths; if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile,zonename,x,y,z,extprofile,instanceid FROM character_ WHERE account_id=%i AND name='%s'", account_id, name), errbuf, &result)) { if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); lengths = mysql_fetch_lengths(result); if (lengths[0] == sizeof(PlayerProfile_Struct)) { memcpy(pp, row[0], sizeof(PlayerProfile_Struct)); if (current_zone) strcpy(current_zone, row[1]); pp->zone_id = GetZoneID(row[1]); pp->x = atof(row[2]); pp->y = atof(row[3]); pp->z = atof(row[4]); pp->zoneInstance = atoi(row[6]); if (pp->x == -1 && pp->y == -1 && pp->z == -1) GetSafePoints(pp->zone_id, GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); if(current_instance) *current_instance = pp->zoneInstance; if(ext) { //SetExtendedProfile handles any conversion SetExtendedProfile(ext, row[5], lengths[5]); } // Retrieve character inventory ret = GetInventory(account_id, name, inv); } else { LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetPlayerProfile. Found: %i, Expected: %i", lengths[0], sizeof(PlayerProfile_Struct)); } } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "GetPlayerProfile query '%s' %s", query, errbuf); } safe_delete_array(query); return ret; } bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { _CP(Database_SetPlayerProfile); char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 affected_rows = 0; bool ret = false; if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { ret = (affected_rows != 0); } if (!ret) { LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); } safe_delete_array(query); return ret; } // Generate SQL for updating player profile uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { *query = new char[396 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 4]; char* end = *query; if (!current_zone) current_zone = pp->zone_id; if (!current_instance) current_instance = pp->zoneInstance; if(strlen(pp->name) == 0) // Sanity check in case pp never loaded return false; end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, profile=\'", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); end += sprintf(end,"\', extprofile=\'"); end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); end += sprintf(end,"\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); return (uint32) (end - (*query)); } // Create appropriate ItemInst class ItemInst* SharedDatabase::CreateItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { const Item_Struct* item = NULL; ItemInst* inst = NULL; item = GetItem(item_id); if (item) { inst = CreateBaseItem(item, charges); inst->PutAugment(this, 0, aug1); inst->PutAugment(this, 1, aug2); inst->PutAugment(this, 2, aug3); inst->PutAugment(this, 3, aug4); inst->PutAugment(this, 4, aug5); } return inst; } // Create appropriate ItemInst class ItemInst* SharedDatabase::CreateItem(const Item_Struct* item, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { ItemInst* inst = NULL; if (item) { inst = CreateBaseItem(item, charges); inst->PutAugment(this, 0, aug1); inst->PutAugment(this, 1, aug2); inst->PutAugment(this, 2, aug3); inst->PutAugment(this, 3, aug4); inst->PutAugment(this, 4, aug5); } return inst; } ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) { ItemInst* inst = NULL; if (item) { // if maxcharges is -1 that means it is an unlimited use item. // set it to 1 charge so that it is usable on creation if (charges == 0 && item->MaxCharges == -1) charges = 1; if(item->CharmFileID != 0 || (item->LoreGroup >= 1000 && item->LoreGroup != -1)) { inst = new EvoItemInst(item, charges); ((EvoItemInst*)inst)->Initialize(this); } else inst = new ItemInst(item, charges); } return inst; } int32 SharedDatabase::DeleteStalePlayerCorpses() { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; uint32 affected_rows = 0; if(RuleB(Zone, EnableShadowrest)) { if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried=0 and " "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) { safe_delete_array(query); return -1; } } else { if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses where (UNIX_TIMESTAMP() - " "UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) { safe_delete_array(query); return -1; } } safe_delete_array(query); return affected_rows; } int32 SharedDatabase::DeleteStalePlayerBackups() { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; uint32 affected_rows = 0; // 1209600 seconds = 2 weeks if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses_backup where (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"), errbuf, 0, &affected_rows)) { safe_delete_array(query); return -1; } safe_delete_array(query); return affected_rows; } bool SharedDatabase::GetCommandSettings(map &commands) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; query = new char[256]; strcpy(query, "SELECT command,access from commands"); commands.clear(); if (RunQuery(query, strlen(query), errbuf, &result)) { safe_delete_array(query); while((row = mysql_fetch_row(result))) { commands[row[0]]=atoi(row[1]); } mysql_free_result(result); return true; } else { cerr << "Error in GetCommands query '" << query << "' " << errbuf << endl; safe_delete_array(query); return false; } return false; } bool SharedDatabase::LoadSkillCaps() { if(skill_caps_mmf) return true; uint32 class_count = PLAYER_CLASS_COUNT; uint32 skill_count = HIGHEST_SKILL + 1; uint32 level_count = HARD_LEVEL_CAP + 1; uint32 size = (class_count * skill_count * level_count * sizeof(uint16)); try { EQEmu::IPCMutex mutex("skill_caps"); mutex.Lock(); skill_caps_mmf = new EQEmu::MemoryMappedFile("shared/skill_caps"); if(skill_caps_mmf->Size() != size) { EQ_EXCEPT("SharedDatabase", "Unable to load skill caps: skill_caps_mmf->Size() != size"); } mutex.Unlock(); } catch(std::exception &ex) { LogFile->write(EQEMuLog::Error, "Error loading skill caps: %s", ex.what()); return false; } return true; } void SharedDatabase::LoadSkillCaps(void *data) { uint32 class_count = PLAYER_CLASS_COUNT; uint32 skill_count = HIGHEST_SKILL + 1; uint32 level_count = HARD_LEVEL_CAP + 1; uint16 *skill_caps_table = reinterpret_cast(data); char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query, MakeAnyLenString(&query, "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"), errbuf, &result)) { safe_delete_array(query); while((row = mysql_fetch_row(result))) { uint8 skillID = atoi(row[0]); uint8 class_ = atoi(row[1]) - 1; uint8 level = atoi(row[2]); uint16 cap = atoi(row[3]); if(skillID >= skill_count || class_ >= class_count || level >= level_count) continue; uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; skill_caps_table[index] = cap; } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", errbuf); safe_delete_array(query); } } uint16 SharedDatabase::GetSkillCap(uint8 Class_, SkillType Skill, uint8 Level) { if(!skill_caps_mmf) { return 0; } if(Class_ == 0) return 0; int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); if(SkillMaxLevel < 1) { SkillMaxLevel = RuleI(Character, MaxLevel); } uint32 class_count = PLAYER_CLASS_COUNT; uint32 skill_count = HIGHEST_SKILL + 1; uint32 level_count = HARD_LEVEL_CAP + 1; if(Class_ > class_count || static_cast(Skill) > skill_count || Level > level_count) { return 0; } if(Level > static_cast(SkillMaxLevel)){ Level = static_cast(SkillMaxLevel); } uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count) + Level; uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); return skill_caps_table[index]; } uint8 SharedDatabase::GetTrainLevel(uint8 Class_, SkillType Skill, uint8 Level) { if(!skill_caps_mmf) { return 0; } if(Class_ == 0) return 0; int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); if (SkillMaxLevel < 1) { SkillMaxLevel = RuleI(Character, MaxLevel); } uint32 class_count = PLAYER_CLASS_COUNT; uint32 skill_count = HIGHEST_SKILL + 1; uint32 level_count = HARD_LEVEL_CAP + 1; if(Class_ > class_count || static_cast(Skill) > skill_count || Level > level_count) { return 0; } uint8 ret = 0; if(Level > static_cast(SkillMaxLevel)) { uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count); uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); for(uint8 x = 0; x < Level; x++){ if(skill_caps_table[index + x]){ ret = x; break; } } } else { uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count); uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); for(int x = 0; x < SkillMaxLevel; x++){ if(skill_caps_table[index + x]){ ret = x; break; } } } if(ret > GetSkillCap(Class_, Skill, Level)) ret = static_cast(GetSkillCap(Class_, Skill, Level)); return ret; } void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) { const char *DSQuery = "SELECT `spellid`, `type` from `damageshieldtypes` WHERE `spellid` > 0 " "AND `spellid` <= %i"; const char *ERR_MYSQLERROR = "Error in LoadDamageShieldTypes: %s %s"; char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query,MakeAnyLenString(&query,DSQuery,iMaxSpellID),errbuf,&result)) { while((row = mysql_fetch_row(result))) { int SpellID = atoi(row[0]); if((SpellID > 0) && (SpellID <= iMaxSpellID)) { sp[SpellID].DamageShieldType = atoi(row[1]); } } mysql_free_result(result); safe_delete_array(query); } else { LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); safe_delete_array(query); } } const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { return NULL; // nothing here for now... database and/or sharemem pulls later } int SharedDatabase::GetMaxSpellID() { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = NULL; MYSQL_RES *result; MYSQL_ROW row; int32 ret = 0; if(RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM spells_new"), errbuf, &result)) { safe_delete_array(query); row = mysql_fetch_row(result); ret = atoi(row[0]); mysql_free_result(result); } else { _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query, errbuf); safe_delete_array(query); ret = -1; } return ret; } void SharedDatabase::LoadSpells(void *data, int max_spells) { SPDat_Spell_Struct *sp = reinterpret_cast(data); char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM spells_new ORDER BY id ASC"), errbuf, &result)) { safe_delete_array(query); int tempid = 0; int counter = 0; while (row = mysql_fetch_row(result)) { tempid = atoi(row[0]); if(tempid >= max_spells) { _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); continue; } ++counter; strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); sp[tempid].range=static_cast(atof(row[9])); sp[tempid].aoerange=static_cast(atof(row[10])); sp[tempid].pushback=static_cast(atof(row[11])); sp[tempid].pushup=static_cast(atof(row[12])); sp[tempid].cast_time=atoi(row[13]); sp[tempid].recovery_time=atoi(row[14]); sp[tempid].recast_time=atoi(row[15]); sp[tempid].buffdurationformula=atoi(row[16]); sp[tempid].buffduration=atoi(row[17]); sp[tempid].AEDuration=atoi(row[18]); sp[tempid].mana=atoi(row[19]); int y=0; for(y=0; y< EFFECT_COUNT;y++) sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value for(y=0; y < EFFECT_COUNT; y++) sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value for(y=0; y< EFFECT_COUNT;y++) sp[tempid].max[y]=atoi(row[44+y]); for(y=0; y< 4;y++) sp[tempid].components[y]=atoi(row[58+y]); for(y=0; y< 4;y++) sp[tempid].component_counts[y]=atoi(row[62+y]); for(y=0; y< 4;y++) sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); for(y=0; y< EFFECT_COUNT;y++) sp[tempid].formula[y]=atoi(row[70+y]); sp[tempid].goodEffect=atoi(row[83]); sp[tempid].Activated=atoi(row[84]); sp[tempid].resisttype=atoi(row[85]); for(y=0; y< EFFECT_COUNT;y++) sp[tempid].effectid[y]=atoi(row[86+y]); sp[tempid].targettype = (SpellTargetType) atoi(row[98]); sp[tempid].basediff=atoi(row[99]); int tmp_skill = atoi(row[100]);; if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) sp[tempid].skill = BEGGING; /* not much better we can do. */ else sp[tempid].skill = (SkillType) tmp_skill; sp[tempid].zonetype=atoi(row[101]); sp[tempid].EnvironmentType=atoi(row[102]); sp[tempid].TimeOfDay=atoi(row[103]); for(y=0; y < PLAYER_CLASS_COUNT;y++) sp[tempid].classes[y]=atoi(row[104+y]); sp[tempid].CastingAnim=atoi(row[120]); sp[tempid].SpellAffectIndex=atoi(row[123]); sp[tempid].disallow_sit=atoi(row[124]); for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); sp[tempid].uninterruptable=atoi(row[146]); sp[tempid].ResistDiff=atoi(row[147]); sp[tempid].RecourseLink = atoi(row[150]); sp[tempid].short_buff_box = atoi(row[154]); sp[tempid].descnum = atoi(row[155]); sp[tempid].effectdescnum = atoi(row[157]); sp[tempid].bonushate=atoi(row[162]); sp[tempid].EndurCost=atoi(row[166]); sp[tempid].EndurTimerIndex=atoi(row[167]); sp[tempid].HateAdded=atoi(row[173]); sp[tempid].EndurUpkeep=atoi(row[174]); sp[tempid].numhits = atoi(row[176]); sp[tempid].pvpresistbase=atoi(row[177]); sp[tempid].pvpresistcalc=atoi(row[178]); sp[tempid].pvpresistcap=atoi(row[179]); sp[tempid].spell_category=atoi(row[180]); sp[tempid].can_mgb=atoi(row[185]); sp[tempid].dispel_flag = atoi(row[186]); sp[tempid].MinResist = atoi(row[189]); sp[tempid].MaxResist = atoi(row[190]); sp[tempid].viral_targets = atoi(row[191]); sp[tempid].viral_timer = atoi(row[192]); sp[tempid].NimbusEffect = atoi(row[193]); sp[tempid].directional_start = (float)atoi(row[194]); sp[tempid].directional_end = (float)atoi(row[195]); sp[tempid].spellgroup=atoi(row[207]); sp[tempid].powerful_flag=atoi(row[209]); sp[tempid].CastRestriction = atoi(row[211]); sp[tempid].AllowRest = atoi(row[212]) != 0; sp[tempid].DamageShieldType = 0; } mysql_free_result(result); LoadDamageShieldTypes(sp, max_spells); } else { _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query, errbuf); safe_delete_array(query); } } void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot_table, uint32 &loot_table_entries) { loot_table_count = 0; max_loot_table = 0; loot_table_entries = 0; const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query, strlen(query), errbuf, &result)) { if(row = mysql_fetch_row(result)) { loot_table_count = static_cast(atoul(row[0])); max_loot_table = static_cast(atoul(row[1])); loot_table_entries = static_cast(atoul(row[2])); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); } } void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_drop, uint32 &loot_drop_entries) { loot_drop_count = 0; max_loot_drop = 0; loot_drop_entries = 0; const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; if(RunQuery(query, strlen(query), errbuf, &result)) { if(row = mysql_fetch_row(result)) { loot_drop_count = static_cast(atoul(row[0])); max_loot_drop = static_cast(atoul(row[1])); loot_drop_entries = static_cast(atoul(row[2])); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); } } void SharedDatabase::LoadLootTables(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); const char *query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin," " loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries" " ON loottable.id = loottable_entries.loottable_id ORDER BY id"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; uint8 loot_table[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)]; LootTable_Struct *lt = reinterpret_cast(loot_table); if(RunQuery(query, strlen(query), errbuf, &result)) { uint32 current_id = 0; uint32 current_entry = 0; while(row = mysql_fetch_row(result)) { uint32 id = static_cast(atoul(row[0])); if(id != current_id) { if(current_id != 0) { hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); } memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); current_entry = 0; current_id = id; lt->mincash = static_cast(atoul(row[1])); lt->maxcash = static_cast(atoul(row[2])); lt->avgcoin = static_cast(atoul(row[3])); } if(current_entry > 128) { continue; } if(!row[4]) { continue; } lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); lt->Entries[current_entry].probability = static_cast(atof(row[8])); ++(lt->NumEntries); ++current_entry; } if(current_id != 0) { hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); } } void SharedDatabase::LoadLootDrops(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); const char *query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; char errbuf[MYSQL_ERRMSG_SIZE]; MYSQL_RES *result; MYSQL_ROW row; uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; LootDrop_Struct *ld = reinterpret_cast(loot_drop); if(RunQuery(query, strlen(query), errbuf, &result)) { uint32 current_id = 0; uint32 current_entry = 0; while(row = mysql_fetch_row(result)) { uint32 id = static_cast(atoul(row[0])); if(id != current_id) { if(current_id != 0) { hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * ld->NumEntries))); } memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); current_entry = 0; current_id = id; } if(current_entry > 1260) { continue; } ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); ld->Entries[current_entry].chance = static_cast(atof(row[4])); ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); ++(ld->NumEntries); ++current_entry; } if(current_id != 0) { hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * ld->NumEntries))); } mysql_free_result(result); } else { LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query, errbuf); } } bool SharedDatabase::LoadLoot() { if(loot_table_mmf || loot_drop_mmf) return true; try { EQEmu::IPCMutex mutex("loot"); mutex.Lock(); loot_table_mmf = new EQEmu::MemoryMappedFile("shared/loot_table"); loot_table_hash = new EQEmu::FixedMemoryVariableHashSet( reinterpret_cast(loot_table_mmf->Get()), loot_table_mmf->Size()); loot_drop_mmf = new EQEmu::MemoryMappedFile("shared/loot_drop"); loot_drop_hash = new EQEmu::FixedMemoryVariableHashSet( reinterpret_cast(loot_drop_mmf->Get()), loot_drop_mmf->Size()); mutex.Unlock(); } catch(std::exception &ex) { LogFile->write(EQEMuLog::Error, "Error loading loot: %s", ex.what()); return false; } return true; } const LootTable_Struct* SharedDatabase::GetLootTable(uint32 loottable_id) { if(!loot_table_hash) return NULL; try { if(loot_table_hash->exists(loottable_id)) { return &loot_table_hash->at(loottable_id); } } catch(std::exception &ex) { LogFile->write(EQEMuLog::Error, "Could not get loot table: %s", ex.what()); } return NULL; } const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { if(!loot_drop_hash) return NULL; try { if(loot_drop_hash->exists(lootdrop_id)) { return &loot_drop_hash->at(lootdrop_id); } } catch(std::exception &ex) { LogFile->write(EQEMuLog::Error, "Could not get loot drop: %s", ex.what()); } return NULL; } void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT inspectmessage FROM character_ WHERE name='%s'", playername), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); memcpy(message, row[0], sizeof(InspectMessage_Struct)); } mysql_free_result(result); } else { cerr << "Error in GetPlayerInspectMessage query '" << query << "' " << errbuf << endl; safe_delete_array(query); } } void SharedDatabase::SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", message->text, playername), errbuf)) { cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << endl; } safe_delete_array(query); } void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; MYSQL_RES *result; MYSQL_ROW row; if (RunQuery(query, MakeAnyLenString(&query, "SELECT BotInspectMessage FROM bots WHERE BotID=%i", botid), errbuf, &result)) { safe_delete_array(query); if (mysql_num_rows(result) == 1) { row = mysql_fetch_row(result); memcpy(message, row[0], sizeof(InspectMessage_Struct)); } mysql_free_result(result); } else { cerr << "Error in GetBotInspectMessage query '" << query << "' " << errbuf << endl; safe_delete_array(query); } } void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { char errbuf[MYSQL_ERRMSG_SIZE]; char *query = 0; if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", message->text, botid), errbuf)) { cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << endl; } safe_delete_array(query); }