Merge pull request #268 from addtheice/RunQueryToDatabaseQuery_zone_tradeskills

Run query to database query zone tradeskills
This commit is contained in:
Alex 2014-10-08 16:31:55 -07:00
commit ee71fa0d56
3 changed files with 355 additions and 490 deletions

View File

@ -681,7 +681,7 @@ public:
inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_);
void TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid); void TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid);
void SendTradeskillDetails(uint32 recipe_id); void SendTradeskillDetails(uint32 recipe_id);
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec); bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill); void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill);

View File

@ -11311,56 +11311,46 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
// some_id = 0 if world combiner, item number otherwise // some_id = 0 if world combiner, item number otherwise
// make where clause segment for container(s) // make where clause segment for container(s)
char containers[30]; std::string containers;
if (tsf->some_id == 0) { if (tsf->some_id == 0)
// world combiner so no item number containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number
snprintf(containers, 29, "= %u", tsf->object_type); else
} containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory
else {
// container in inventory
snprintf(containers, 29, "in (%u,%u)", tsf->object_type, tsf->some_id);
}
char *query = 0;
char buf[5500]; //gotta be big enough for 500 IDs
std::string favoriteIDs; //gotta be big enough for 500 IDs
bool first = true; bool first = true;
uint16 r;
char *pos = buf;
//Assumes item IDs are <10 characters long //Assumes item IDs are <10 characters long
for (r = 0; r < 500; r++) { for (uint16 favoriteIndex = 0; favoriteIndex < 500; ++favoriteIndex) {
if (tsf->favorite_recipes[r] == 0) if (tsf->favorite_recipes[favoriteIndex] == 0)
continue; continue;
if (first) { if (first) {
pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); favoriteIDs += StringFormat("%u", tsf->favorite_recipes[favoriteIndex]);
first = false; first = false;
} }
else { else
pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); favoriteIDs += StringFormat(",%u", tsf->favorite_recipes[favoriteIndex]);
}
} }
if (first) //no favorites.... if (first) //no favorites....
return; return;
//To be a good kid, I should move this SQL somewhere else... const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
//but im lazy right now, so it stays here "SUM(tre.componentcount), crl.madecount,tr.tradeskill "
uint32 qlen = 0;
qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill "
"FROM tradeskill_recipe AS tr " "FROM tradeskill_recipe AS tr "
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " "LEFT JOIN (SELECT recipe_id, madecount "
"FROM char_recipe_list "
"WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
"WHERE tr.enabled <> 0 AND tr.id IN (%s) " "WHERE tr.enabled <> 0 AND tr.id IN (%s) "
" AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " "AND tr.must_learn & 0x20 <> 0x20 AND "
"((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) "
"OR (tr.must_learn & 0x3 = 0)) "
"GROUP BY tr.id " "GROUP BY tr.id "
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
" LIMIT 100 ", CharacterID(), buf, containers); "LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str());
TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); TradeskillSearchResults(query, tsf->object_type, tsf->some_id);
safe_delete_array(query);
return; return;
} }
@ -11389,36 +11379,33 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id);
} }
char *query = 0; std::string searchClause;
char searchclause[140]; //2X rss->query + SQL crap
//omit the rlike clause if query is empty //omit the rlike clause if query is empty
if (rss->query[0] != 0) { if (rss->query[0] != 0) {
char buf[120]; //larger than 2X rss->query char buf[120]; //larger than 2X rss->query
database.DoEscapeString(buf, rss->query, strlen(rss->query)); database.DoEscapeString(buf, rss->query, strlen(rss->query));
searchClause = StringFormat("name rlike '%s' AND", buf);
snprintf(searchclause, 139, "name rlike '%s' AND", buf);
} }
else {
searchclause[0] = '\0';
}
uint32 qlen = 0;
//arbitrary limit of 200 recipes, makes sense to me. //arbitrary limit of 200 recipes, makes sense to me.
qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
"FROM tradeskill_recipe AS tr " "FROM tradeskill_recipe AS tr "
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id " "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id "
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " "LEFT JOIN (SELECT recipe_id, madecount "
"FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
"WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " "WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 "
" AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " "AND tr.must_learn & 0x20 <> 0x20 "
"AND ((tr.must_learn & 0x3 <> 0 "
"AND crl.madecount IS NOT NULL) "
"OR (tr.must_learn & 0x3 = 0)) "
"GROUP BY tr.id " "GROUP BY tr.id "
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
" LIMIT 200 " "LIMIT 200 ",
, CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); CharacterID(), searchClause.c_str(),
rss->mintrivial, rss->maxtrivial, containers);
TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); TradeskillSearchResults(query, rss->object_type, rss->some_id);
safe_delete_array(query);
return; return;
} }

View File

@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
return; return;
} }
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qlen = 0;
uint8 qcount = 0;
//pull the list of components //pull the list of components
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount " std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
"FROM tradeskill_recipe_entries AS tre " "FROM tradeskill_recipe_entries AS tre "
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id); "WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
rac->recipe_id);
if (!database.RunQuery(query, qlen, errbuf, &result)) { auto results = database.QueryDatabase(query);
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf); if (!results.Success()) {
safe_delete_array(query); LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
user->QueuePacket(outapp); user->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
return; return;
} }
safe_delete_array(query);
qcount = mysql_num_rows(result); if(results.RowCount() < 1) {
if(qcount < 1) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned"); LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned");
user->QueuePacket(outapp); user->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
return; return;
} }
if(qcount > 10) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", qcount); if(results.RowCount() > 10) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", results.RowCount());
user->QueuePacket(outapp); user->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
return; return;
@ -466,17 +456,15 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
uint8 counts[10]; uint8 counts[10];
memset(counts, 0, sizeof(counts)); memset(counts, 0, sizeof(counts));
//search for all the items in their inventory //search for all the items in their inventory
Inventory& user_inv = user->GetInv(); Inventory& user_inv = user->GetInv();
uint8 count = 0; uint8 count = 0;
uint8 needcount = 0; uint8 needcount = 0;
uint8 r,k;
std::list<int> MissingItems; std::list<int> MissingItems;
for(r = 0; r < qcount; r++) { uint8 needItemIndex = 0;
row = mysql_fetch_row(result); for (auto row = results.begin(); row != results.end(); ++row, ++needItemIndex) {
uint32 item = (uint32)atoi(row[0]); uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]); uint8 num = (uint8) atoi(row[1]);
@ -491,10 +479,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
MissingItems.push_back(item); MissingItems.push_back(item);
//dont start deleting anything until we have found it all. //dont start deleting anything until we have found it all.
items[r] = item; items[needItemIndex] = item;
counts[r] = num; counts[needItemIndex] = num;
} }
mysql_free_result(result);
//make sure we found it all... //make sure we found it all...
if(count != needcount) if(count != needcount)
@ -520,12 +507,12 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
//remove all the items from the players inventory, with updates... //remove all the items from the players inventory, with updates...
int16 slot; int16 slot;
for(r = 0; r < qcount; r++) { for(uint8 r = 0; r < results.RowCount(); r++) {
if(items[r] == 0 || counts[r] == 0) if(items[r] == 0 || counts[r] == 0)
continue; //skip empties, could prolly break here continue; //skip empties, could prolly break here
//we have to loop here to delete 1 at a time in case its in multiple stacks. //we have to loop here to delete 1 at a time in case its in multiple stacks.
for(k = 0; k < counts[r]; k++) { for(uint8 k = 0; k < counts[r]; k++) {
slot = user_inv.HasItem(items[r], 1, invWherePersonal); slot = user_inv.HasItem(items[r], 1, invWherePersonal);
if (slot == INVALID_INDEX) { if (slot == INVALID_INDEX) {
//WTF... I just checked this above, but just to be sure... //WTF... I just checked this above, but just to be sure...
@ -539,19 +526,14 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
const ItemInst* inst = user_inv.GetItem(slot); const ItemInst* inst = user_inv.GetItem(slot);
if (inst && !inst->IsStackable()) if (inst && !inst->IsStackable())
{
user->DeleteItemInInventory(slot, 0, true); user->DeleteItemInInventory(slot, 0, true);
}
else else
{
user->DeleteItemInInventory(slot, 1, true); user->DeleteItemInInventory(slot, 1, true);
} }
} }
}
//otherwise, we found it all... //otherwise, we found it all...
outp->reply_code = 0x00000000; //success for finding it... outp->reply_code = 0x00000000; //success for finding it...
user->QueuePacket(outapp); user->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
@ -654,34 +636,26 @@ SkillUseTypes Object::TypeToSkill(uint32 type)
return TradeskillUnknown; return TradeskillUnknown;
} }
void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) { void Client::TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid) {
char errbuf[MYSQL_ERRMSG_SIZE]; auto results = database.QueryDatabase(query);
MYSQL_RES *result; if (!results.Success()) {
MYSQL_ROW row; LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf);
return; return;
} }
uint8 qcount = 0; if(results.RowCount() < 1)
return; //search gave no results... not an error
qcount = mysql_num_rows(result); if(results.ColumnCount() != 6) {
if(qcount < 1) { LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str());
//search gave no results... not an error
return;
}
if(mysql_num_fields(result) != 6) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query);
return; return;
} }
uint8 r; for(auto row = results.begin(); row != results.end(); ++row) {
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr) if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr)
continue; continue;
uint32 recipe = (uint32)atoi(row[0]); uint32 recipe = (uint32)atoi(row[0]);
const char *name = row[1]; const char *name = row[1];
uint32 trivial = (uint32) atoi(row[2]); uint32 trivial = (uint32) atoi(row[2]);
@ -691,14 +665,10 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi
// Skip the recipes that exceed the threshold in skill difference // Skip the recipes that exceed the threshold in skill difference
// Recipes that have either been made before or were // Recipes that have either been made before or were
// explicitly learned are excempt from that limit // explicitly learned are excempt from that limit
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) { if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)
if (((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) && ((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
&& row[4] == nullptr) && row[4] == nullptr)
{
continue; continue;
}
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct));
RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer;
@ -711,39 +681,30 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi
strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name)); strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name));
FastQueuePacket(&outapp); FastQueuePacket(&outapp);
} }
mysql_free_result(result);
} }
void Client::SendTradeskillDetails(uint32 recipe_id) { void Client::SendTradeskillDetails(uint32 recipe_id) {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qlen = 0;
uint8 qcount = 0;
//pull the list of components //pull the list of components
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount,i.icon,i.Name " std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
"FROM tradeskill_recipe_entries AS tre " "FROM tradeskill_recipe_entries AS tre "
"LEFT JOIN items AS i ON tre.item_id = i.id " "LEFT JOIN items AS i ON tre.item_id = i.id "
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", recipe_id); "WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
recipe_id);
if (!database.RunQuery(query, qlen, errbuf, &result)) { auto results = database.QueryDatabase(query);
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query, errbuf); if (!results.Success()) {
safe_delete_array(query); LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return; return;
} }
safe_delete_array(query);
qcount = mysql_num_rows(result); if(results.RowCount() < 1) {
if(qcount < 1) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned"); LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned");
return; return;
} }
if(qcount > 10) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", qcount); if(results.RowCount() > 10) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", results.RowCount());
return; return;
} }
@ -773,20 +734,18 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
uint32 len; uint32 len;
uint32 datalen = 0; uint32 datalen = 0;
uint8 count = 0; uint8 count = 0;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result); for(auto row = results.begin(); row != results.end(); ++row) {
//watch for references to items which are not in the //watch for references to items which are not in the
//items table, which the left join will make nullptr... //items table, which the left join will make nullptr...
if(row[2] == nullptr || row[3] == nullptr) { if(row[2] == nullptr || row[3] == nullptr)
continue; continue;
}
uint32 item = (uint32)atoi(row[0]); uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]); uint8 num = (uint8) atoi(row[1]);
uint32 icon = (uint32) atoi(row[2]); uint32 icon = (uint32) atoi(row[2]);
const char *name = row[3]; const char *name = row[3];
len = strlen(name); len = strlen(name);
if(len > 63) if(len > 63)
@ -816,7 +775,6 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
} }
} }
mysql_free_result(result);
//now move the item data over top of the FFFFs //now move the item data over top of the FFFFs
uint8 dist = sizeof(uint32) * (10 - count); uint8 dist = sizeof(uint32) * (10 - count);
@ -1191,241 +1149,203 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id,
uint32 char_id, DBTradeskillRecipe_Struct *spec) uint32 char_id, DBTradeskillRecipe_Struct *spec)
{ {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
char buf2[4096];
uint32 sum = 0; std::string containers;// make where clause segment for container(s)
uint32 count = 0; if (some_id == 0)
uint32 qcount = 0; containers = StringFormat("= %u", c_type); // world combiner so no item number
uint32 qlen = 0; else
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
// make where clause segment for container(s)
char containers[30];
if (some_id == 0) {
// world combiner so no item number
snprintf(containers,29, "= %u", c_type);
} else {
// container in inventory
snprintf(containers,29, "in (%u,%u)", c_type, some_id);
}
buf2[0] = '\0';
//Could prolly watch for stacks in this loop and handle them properly... //Could prolly watch for stacks in this loop and handle them properly...
//just increment sum and count accordingly //just increment sum and count accordingly
bool first = true; bool first = true;
uint8 i; std::string buf2;
char *pos = buf2; uint32 count = 0;
for (i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size uint32 sum = 0;
for (uint8 i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size
const ItemInst* inst = container->GetItem(i); const ItemInst* inst = container->GetItem(i);
if (inst) { if (!inst)
continue;
const Item_Struct* item = GetItem(inst->GetItem()->ID); const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (item) { if (!item)
continue;
if(first) { if(first) {
pos += snprintf(pos, 19, "%d", item->ID); buf2 += StringFormat("%d", item->ID);
first = false; first = false;
} else { } else
pos += snprintf(pos, 19, ",%d", item->ID); buf2 += StringFormat(",%d", item->ID);
}
sum += item->ID; sum += item->ID;
count++; count++;
} }
}
}
*pos = '\0';
if(count < 1) { if(count == 0)
return(false); //no items == no recipe return false; //no items == no recipe
}
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id " std::string query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre " "FROM tradeskill_recipe_entries AS tre "
"INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) " "INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) "
"WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) " "WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) "
"OR ( tre.item_id %s AND tre.iscontainer=1 ))" "OR ( tre.item_id %s AND tre.iscontainer=1 ))"
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum); "AND sum(tre.item_id * tre.componentcount) = %u",
buf2.c_str(), containers.c_str(), count, sum);
if (!RunQuery(query, qlen, errbuf, &result)) { auto results = QueryDatabase(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query); if (!results.Success()) {
safe_delete_array(query); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str());
return(false); return false;
} }
safe_delete_array(query);
qcount = mysql_num_rows(result); if (results.RowCount() > 1) {
if(qcount > 1) {
//multiple recipes, partial match... do an extra query to get it exact. //multiple recipes, partial match... do an extra query to get it exact.
//this happens when combining components for a smaller recipe //this happens when combining components for a smaller recipe
//which is completely contained within another recipe //which is completely contained within another recipe
first = true; first = true;
pos = buf2; uint32 index = 0;
for (i = 0; i < qcount; i++) { buf2 = "";
row = mysql_fetch_row(result); for (auto row = results.begin(); row != results.end(); ++row, ++index) {
uint32 recipeid = (uint32)atoi(row[0]); uint32 recipeid = (uint32)atoi(row[0]);
if(first) { if(first) {
pos += snprintf(pos, 19, "%u", recipeid); buf2 += StringFormat("%u", recipeid);
first = false; first = false;
} else { } else
pos += snprintf(pos, 19, ",%u", recipeid); buf2 += StringFormat(",%u", recipeid);
}
//length limit on buf2 //length limit on buf2
if(i == 214) { //Maximum number of recipe matches (19 * 215 = 4096) if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096)
LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", i + 1, qcount); LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount());
break; break;
} }
} }
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id" query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre " "FROM tradeskill_recipe_entries AS tre "
"WHERE tre.recipe_id IN (%s) " "WHERE tre.recipe_id IN (%s) "
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum); "AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum);
auto results = QueryDatabase(query);
if (!RunQuery(query, qlen, errbuf, &result)) { if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
safe_delete_array(query); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); return false;
return(false);
} }
safe_delete_array(query);
qcount = mysql_num_rows(result);
} }
if(qcount < 1) if (results.RowCount() < 1)
return(false); return false;
if(qcount > 1) if(results.RowCount() > 1) {
{
//The recipe is not unique, so we need to compare the container were using. //The recipe is not unique, so we need to compare the container were using.
uint32 containerId = 0; uint32 containerId = 0;
if(some_id) { //Standard container if(some_id) //Standard container
containerId = some_id; containerId = some_id;
} else if(c_type)//World container
else if(c_type) { //World container
containerId = c_type; containerId = c_type;
} else //Invalid container
else { //Invalid container return false;
return(false);
query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre "
"WHERE tre.recipe_id IN (%s) "
"AND tre.item_id = %u;", buf2.c_str(), containerId);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
return false;
} }
qlen = MakeAnyLenString(&query,"SELECT tre.recipe_id FROM tradeskill_recipe_entries as tre WHERE tre.recipe_id IN (%s)" if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container
" AND tre.item_id = %u;",buf2,containerId);
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
uint32 resultRowTotal = mysql_num_rows(result);
if(resultRowTotal == 0) { //Recipe contents matched more than 1 recipe, but not in this container
LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!"); LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!");
return(false); return false;
}
if(resultRowTotal > 1) { //Recipe contents matched more than 1 recipe in this container
LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", resultRowTotal, containerId);
}
} }
row = mysql_fetch_row(result); if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container
LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId);
}
auto row = results.begin();
uint32 recipe_id = (uint32)atoi(row[0]); uint32 recipe_id = (uint32)atoi(row[0]);
mysql_free_result(result);
//Right here we verify that we actually have ALL of the tradeskill components.. //Right here we verify that we actually have ALL of the tradeskill components..
//instead of part which is possible with experimentation. //instead of part which is possible with experimentation.
//This is here because something's up with the query above.. it needs to be rethought out //This is here because something's up with the query above.. it needs to be rethought out
bool has_components = true; bool has_components = true;
char TSerrbuf[MYSQL_ERRMSG_SIZE]; query = StringFormat("SELECT item_id, componentcount "
char *TSquery = 0; "FROM tradeskill_recipe_entries "
MYSQL_RES *TSresult; "WHERE recipe_id = %i AND componentcount > 0",
MYSQL_ROW TSrow; recipe_id);
if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) { results = QueryDatabase(query);
while((TSrow = mysql_fetch_row(TSresult))!=nullptr) { if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
}
if (results.RowCount() == 0)
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
for (auto row = results.begin(); row != results.end(); ++row) {
int ccnt = 0; int ccnt = 0;
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) { for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
const ItemInst* inst = container->GetItem(x); const ItemInst* inst = container->GetItem(x);
if(inst){ if(!inst)
continue;
const Item_Struct* item = GetItem(inst->GetItem()->ID); const Item_Struct* item = GetItem(inst->GetItem()->ID);
if (item) { if (!item)
if(item->ID == atoi(TSrow[0])){ continue;
if(item->ID == atoi(row[0]))
ccnt++; ccnt++;
} }
}
}
}
if(ccnt != atoi(TSrow[1]))
has_components = false;
}
mysql_free_result(TSresult);
} else {
LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", TSquery, TSerrbuf);
}
safe_delete_array(TSquery);
if(has_components == false){
if(ccnt != atoi(row[1]))
return false; return false;
} }
return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec)); return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
} }
bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id,
uint32 char_id, DBTradeskillRecipe_Struct *spec) uint32 char_id, DBTradeskillRecipe_Struct *spec)
{ {
char errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
char *query = 0;
uint32 qcount = 0;
uint32 qlen;
// make where clause segment for container(s) // make where clause segment for container(s)
char containers[30]; std::string containers;
if (some_id == 0) { if (some_id == 0)
// world combiner so no item number containers = StringFormat("= %u", c_type); // world combiner so no item number
snprintf(containers,29, "= %u", c_type); else
} else { containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
// container in inventory
snprintf(containers,29, "in (%u,%u)", c_type, some_id);
}
qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded," std::string query = StringFormat("SELECT tr.id, tr.tradeskill, tr.skillneeded, "
" tr.trivial, tr.nofail, tr.replace_container, tr.name, tr.must_learn, tr.quest, crl.madecount" "tr.trivial, tr.nofail, tr.replace_container, "
" FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre" "tr.name, tr.must_learn, tr.quest, crl.madecount "
"FROM tradeskill_recipe AS tr "
"INNER JOIN tradeskill_recipe_entries AS tre "
"ON tr.id = tre.recipe_id " "ON tr.id = tre.recipe_id "
" LEFT JOIN (SELECT recipe_id, madecount from char_recipe_list WHERE char_id = %u) AS crl " "LEFT JOIN (SELECT recipe_id, madecount "
"FROM char_recipe_list WHERE char_id = %u) AS crl "
"ON tr.id = crl.recipe_id " "ON tr.id = crl.recipe_id "
"WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled " "WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
" GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers); "GROUP BY tr.id",
char_id, (unsigned long)recipe_id, containers.c_str());
if (!RunQuery(query, qlen, errbuf, &result)) { auto results = QueryDatabase(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query); if (!results.Success()) {
safe_delete_array(query); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
return(false); return false;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount != 1) {
//just not found i guess..
return(false);
} }
row = mysql_fetch_row(result); if(results.RowCount() != 1)
return false;//just not found i guess..
auto row = results.begin();
spec->tradeskill = (SkillUseTypes)atoi(row[1]); spec->tradeskill = (SkillUseTypes)atoi(row[1]);
spec->skill_needed = (int16)atoi(row[2]); spec->skill_needed = (int16)atoi(row[2]);
spec->trivial = (uint16)atoi(row[3]); spec->trivial = (uint16)atoi(row[3]);
@ -1434,6 +1354,7 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id
spec->name = row[6]; spec->name = row[6];
spec->must_learn = (uint8)atoi(row[7]); spec->must_learn = (uint8)atoi(row[7]);
spec->quest = atoi(row[8]) ? true : false; spec->quest = atoi(row[8]) ? true : false;
if (row[9] == nullptr) { if (row[9] == nullptr) {
spec->has_learnt = false; spec->has_learnt = false;
spec->madecount = 0; spec->madecount = 0;
@ -1442,141 +1363,109 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id
spec->madecount = (uint32)atoul(row[9]); spec->madecount = (uint32)atoul(row[9]);
} }
spec->recipe_id = recipe_id; spec->recipe_id = recipe_id;
mysql_free_result(result);
//Pull the on-success items... //Pull the on-success items...
qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries" query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries "
"WHERE successcount > 0 AND recipe_id = %u", recipe_id); "WHERE successcount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (!RunQuery(query, qlen, errbuf, &result)) { if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
safe_delete_array(query); return false;
return(false);
} }
safe_delete_array(query);
qcount = mysql_num_rows(result); if(results.RowCount() < 1) {
if(qcount < 1) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned"); LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned");
return(false); return false;
} }
uint8 r;
spec->onsuccess.clear(); spec->onsuccess.clear();
for(r = 0; r < qcount; r++) { for(auto row = results.begin(); row != results.end(); ++row) {
row = mysql_fetch_row(result);
uint32 item = (uint32)atoi(row[0]); uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]); uint8 num = (uint8) atoi(row[1]);
spec->onsuccess.push_back(std::pair<uint32,uint8>(item, num)); spec->onsuccess.push_back(std::pair<uint32,uint8>(item, num));
} }
mysql_free_result(result);
//Pull the on-fail items...
qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries"
" WHERE failcount>0 AND recipe_id=%u", recipe_id);
spec->onfail.clear(); spec->onfail.clear();
if (RunQuery(query, qlen, errbuf, &result)) { //Pull the on-fail items...
query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries "
qcount = mysql_num_rows(result); "WHERE failcount > 0 AND recipe_id = %u", recipe_id);
uint8 r; results = QueryDatabase(query);
for(r = 0; r < qcount; r++) { if (results.Success())
row = mysql_fetch_row(result); for(auto row = results.begin(); row != results.end(); ++row) {
uint32 item = (uint32)atoi(row[0]); uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]); uint8 num = (uint8) atoi(row[1]);
spec->onfail.push_back(std::pair<uint32,uint8>(item, num)); spec->onfail.push_back(std::pair<uint32,uint8>(item, num));
} }
mysql_free_result(result);
}
// Pull the salvage list
qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id);
spec->salvage.clear(); spec->salvage.clear();
// Don't bother with the query if TS is nofail // Don't bother with the query if TS is nofail
if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) { if (spec->nofail)
qcount = mysql_num_rows(result); return true;
uint8 r;
for(r = 0; r < qcount; r++) { // Pull the salvage list
row = mysql_fetch_row(result); query = StringFormat("SELECT item_id, salvagecount "
"FROM tradeskill_recipe_entries "
"WHERE salvagecount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (results.Success())
for(auto row = results.begin(); row != results.begin(); ++row) {
uint32 item = (uint32)atoi(row[0]); uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8)atoi(row[1]); uint8 num = (uint8)atoi(row[1]);
spec->salvage.push_back(std::pair<uint32,uint8>(item, num)); spec->salvage.push_back(std::pair<uint32,uint8>(item, num));
} }
mysql_free_result(result);
return true;
} }
safe_delete_array(query); void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madeCount)
return(true);
}
void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount)
{ {
char *query = 0; std::string query = StringFormat("INSERT INTO char_recipe_list "
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
"SET recipe_id = %u, char_id = %u, madecount = %u " "SET recipe_id = %u, char_id = %u, madecount = %u "
" ON DUPLICATE KEY UPDATE madecount = %u;" "ON DUPLICATE KEY UPDATE madecount = %u;",
, recipe_id, char_id, madecount, madecount); recipe_id, char_id, madeCount, madeCount);
auto results = QueryDatabase(query);
if (!RunQuery(query, qlen, errbuf)) { if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query, errbuf); LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
safe_delete_array(query);
} }
void Client::LearnRecipe(uint32 recipeID) void Client::LearnRecipe(uint32 recipeID)
{ {
char *query = 0; std::string query = StringFormat("SELECT tr.name, crl.madecount "
uint32 qlen; "FROM tradeskill_recipe AS tr "
uint32 qcount = 0; "LEFT JOIN (SELECT recipe_id, madecount "
char errbuf[MYSQL_ERRMSG_SIZE]; "FROM char_recipe_list WHERE char_id = %u) AS crl "
MYSQL_RES *result;
MYSQL_ROW row;
qlen = MakeAnyLenString(&query, "SELECT tr.name, crl.madecount "
" FROM tradeskill_recipe as tr "
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl "
"ON tr.id = crl.recipe_id " "ON tr.id = crl.recipe_id "
"WHERE tr.id = %u ;", CharacterID(), recipeID); "WHERE tr.id = %u ;", CharacterID(), recipeID);
auto results = database.QueryDatabase(query);
if (!database.RunQuery(query, qlen, errbuf, &result)) { if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query, errbuf); LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
safe_delete_array(query);
return; return;
} }
qcount = mysql_num_rows(result); if (results.RowCount() != 1) {
if (qcount != 1) { LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount());
LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, qcount);
mysql_free_result(result);
safe_delete_array(query);
return; return;
} }
safe_delete_array(query);
row = mysql_fetch_row(result); auto row = results.begin();
if (row[0] == nullptr)
return;
if (row != nullptr && row[0] != nullptr) {
// Only give Learn message if character doesn't know the recipe // Only give Learn message if character doesn't know the recipe
if (row[1] == nullptr) { if (row[1] != nullptr)
return;
Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]); Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]);
// Actually learn the recipe now // Actually learn the recipe now
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list " query = StringFormat("INSERT INTO char_recipe_list "
"SET recipe_id = %u, char_id = %u, madecount = 0 " "SET recipe_id = %u, char_id = %u, madecount = 0 "
" ON DUPLICATE KEY UPDATE madecount = madecount;" "ON DUPLICATE KEY UPDATE madecount = madecount;",
, recipeID, CharacterID()); recipeID, CharacterID());
results = database.QueryDatabase(query);
if (!database.RunQuery(query, qlen, errbuf)) { if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf); LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
safe_delete_array(query);
}
}
mysql_free_result(result);
} }
@ -1622,33 +1511,22 @@ bool Client::CanIncreaseTradeskill(SkillUseTypes tradeskill) {
bool ZoneDatabase::EnableRecipe(uint32 recipe_id) bool ZoneDatabase::EnableRecipe(uint32 recipe_id)
{ {
char *query = 0; std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 1 "
uint32 qlen; "WHERE id = %u;", recipe_id);
char errbuf[MYSQL_ERRMSG_SIZE]; auto results = QueryDatabase(query);
uint32 affected_rows = 0; if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 1 WHERE id = %u;", recipe_id); return results.RowsAffected() > 0;
if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return (affected_rows > 0);
} }
bool ZoneDatabase::DisableRecipe(uint32 recipe_id) bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
{ {
char *query = 0; std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 0 "
uint32 qlen; "WHERE id = %u;", recipe_id);
char errbuf[MYSQL_ERRMSG_SIZE]; auto results = QueryDatabase(query);
uint32 affected_rows = 0; if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 0 WHERE id = %u;", recipe_id); return results.RowsAffected() > 0;
if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) {
LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
return (affected_rows > 0);
} }