mirror of
https://github.com/EQEmu/Server.git
synced 2026-03-07 11:22:24 +00:00
Merge pull request #268 from addtheice/RunQueryToDatabaseQuery_zone_tradeskills
Run query to database query zone tradeskills
This commit is contained in:
commit
ee71fa0d56
@ -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);
|
||||||
|
|||||||
@ -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;
|
"FROM tradeskill_recipe AS tr "
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill "
|
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
||||||
" FROM tradeskill_recipe AS tr "
|
"LEFT JOIN (SELECT recipe_id, madecount "
|
||||||
" LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
"FROM char_recipe_list "
|
||||||
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
"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 "
|
||||||
" GROUP BY tr.id "
|
"((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) "
|
||||||
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
"OR (tr.must_learn & 0x3 = 0)) "
|
||||||
" LIMIT 100 ", CharacterID(), buf, containers);
|
"GROUP BY tr.id "
|
||||||
|
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||||
|
"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, "
|
||||||
" FROM tradeskill_recipe AS tr "
|
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
|
||||||
" LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
"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 "
|
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id "
|
||||||
" WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 "
|
"LEFT JOIN (SELECT recipe_id, madecount "
|
||||||
" AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) "
|
"FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
||||||
" GROUP BY tr.id "
|
"WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 "
|
||||||
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
"AND tr.must_learn & 0x20 <> 0x20 "
|
||||||
" LIMIT 200 "
|
"AND ((tr.must_learn & 0x3 <> 0 "
|
||||||
, CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers);
|
"AND crl.madecount IS NOT NULL) "
|
||||||
|
"OR (tr.must_learn & 0x3 = 0)) "
|
||||||
TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id);
|
"GROUP BY tr.id "
|
||||||
|
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||||
safe_delete_array(query);
|
"LIMIT 200 ",
|
||||||
|
CharacterID(), searchClause.c_str(),
|
||||||
|
rss->mintrivial, rss->maxtrivial, containers);
|
||||||
|
TradeskillSearchResults(query, rss->object_type, rss->some_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pull the list of components
|
||||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
|
||||||
MYSQL_RES *result;
|
"FROM tradeskill_recipe_entries AS tre "
|
||||||
MYSQL_ROW row;
|
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
|
||||||
char *query = 0;
|
rac->recipe_id);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
uint32 qlen = 0;
|
if (!results.Success()) {
|
||||||
uint8 qcount = 0;
|
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
|
|
||||||
//pull the list of components
|
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount "
|
|
||||||
" FROM tradeskill_recipe_entries AS tre "
|
|
||||||
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id);
|
|
||||||
|
|
||||||
if (!database.RunQuery(query, qlen, errbuf, &result)) {
|
|
||||||
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf);
|
|
||||||
safe_delete_array(query);
|
|
||||||
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];
|
//pull the list of components
|
||||||
MYSQL_RES *result;
|
std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
|
||||||
MYSQL_ROW row;
|
"FROM tradeskill_recipe_entries AS tre "
|
||||||
char *query = 0;
|
"LEFT JOIN items AS i ON tre.item_id = i.id "
|
||||||
|
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
|
||||||
uint32 qlen = 0;
|
recipe_id);
|
||||||
uint8 qcount = 0;
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
//pull the list of components
|
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
|
|
||||||
" FROM tradeskill_recipe_entries AS tre "
|
|
||||||
" LEFT JOIN items AS i ON tre.item_id = i.id "
|
|
||||||
" WHERE tre.componentcount > 0 AND tre.recipe_id=%u", recipe_id);
|
|
||||||
|
|
||||||
if (!database.RunQuery(query, qlen, errbuf, &result)) {
|
|
||||||
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query, errbuf);
|
|
||||||
safe_delete_array(query);
|
|
||||||
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,249 +1149,212 @@ 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)
|
||||||
const Item_Struct* item = GetItem(inst->GetItem()->ID);
|
continue;
|
||||||
if (item) {
|
|
||||||
if(first) {
|
|
||||||
pos += snprintf(pos, 19, "%d", item->ID);
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
pos += snprintf(pos, 19, ",%d", item->ID);
|
|
||||||
}
|
|
||||||
sum += item->ID;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*pos = '\0';
|
|
||||||
|
|
||||||
if(count < 1) {
|
const Item_Struct* item = GetItem(inst->GetItem()->ID);
|
||||||
return(false); //no items == no recipe
|
if (!item)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(first) {
|
||||||
|
buf2 += StringFormat("%d", item->ID);
|
||||||
|
first = false;
|
||||||
|
} else
|
||||||
|
buf2 += StringFormat(",%d", item->ID);
|
||||||
|
|
||||||
|
sum += item->ID;
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id "
|
if(count == 0)
|
||||||
" FROM tradeskill_recipe_entries AS tre"
|
return false; //no items == no recipe
|
||||||
" 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 )"
|
|
||||||
" OR ( tre.item_id %s AND tre.iscontainer=1 ))"
|
|
||||||
" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
|
|
||||||
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum);
|
|
||||||
|
|
||||||
if (!RunQuery(query, qlen, errbuf, &result)) {
|
std::string query = StringFormat("SELECT tre.recipe_id "
|
||||||
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
|
"FROM tradeskill_recipe_entries AS tre "
|
||||||
safe_delete_array(query);
|
"INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) "
|
||||||
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
|
"WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) "
|
||||||
return(false);
|
"OR ( tre.item_id %s AND tre.iscontainer=1 ))"
|
||||||
}
|
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
|
||||||
safe_delete_array(query);
|
"AND sum(tre.item_id * tre.componentcount) = %u",
|
||||||
|
buf2.c_str(), containers.c_str(), count, sum);
|
||||||
qcount = mysql_num_rows(result);
|
auto results = QueryDatabase(query);
|
||||||
if(qcount > 1) {
|
if (!results.Success()) {
|
||||||
//multiple recipes, partial match... do an extra query to get it exact.
|
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str());
|
||||||
//this happens when combining components for a smaller recipe
|
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str());
|
||||||
//which is completely contained within another recipe
|
return false;
|
||||||
|
|
||||||
first = true;
|
|
||||||
pos = buf2;
|
|
||||||
for (i = 0; i < qcount; i++) {
|
|
||||||
row = mysql_fetch_row(result);
|
|
||||||
uint32 recipeid = (uint32)atoi(row[0]);
|
|
||||||
if(first) {
|
|
||||||
pos += snprintf(pos, 19, "%u", recipeid);
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
pos += snprintf(pos, 19, ",%u", recipeid);
|
|
||||||
}
|
|
||||||
//length limit on buf2
|
|
||||||
if(i == 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);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id"
|
|
||||||
" FROM tradeskill_recipe_entries AS tre"
|
|
||||||
" WHERE tre.recipe_id IN (%s)"
|
|
||||||
" GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
|
|
||||||
" AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
qcount = mysql_num_rows(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(qcount < 1)
|
if (results.RowCount() > 1) {
|
||||||
return(false);
|
//multiple recipes, partial match... do an extra query to get it exact.
|
||||||
|
//this happens when combining components for a smaller recipe
|
||||||
|
//which is completely contained within another recipe
|
||||||
|
first = true;
|
||||||
|
uint32 index = 0;
|
||||||
|
buf2 = "";
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
|
||||||
|
uint32 recipeid = (uint32)atoi(row[0]);
|
||||||
|
if(first) {
|
||||||
|
buf2 += StringFormat("%u", recipeid);
|
||||||
|
first = false;
|
||||||
|
} else
|
||||||
|
buf2 += StringFormat(",%u", recipeid);
|
||||||
|
|
||||||
if(qcount > 1)
|
//length limit on buf2
|
||||||
{
|
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.", index + 1, results.RowCount());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query = StringFormat("SELECT tre.recipe_id "
|
||||||
|
"FROM tradeskill_recipe_entries AS tre "
|
||||||
|
"WHERE tre.recipe_id IN (%s) "
|
||||||
|
"GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u "
|
||||||
|
"AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum);
|
||||||
|
auto 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.RowCount() < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
row = mysql_fetch_row(result);
|
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()) {
|
||||||
int ccnt = 0;
|
LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
|
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
|
||||||
const ItemInst* inst = container->GetItem(x);
|
}
|
||||||
if(inst){
|
|
||||||
const Item_Struct* item = GetItem(inst->GetItem()->ID);
|
|
||||||
if (item) {
|
|
||||||
if(item->ID == atoi(TSrow[0])){
|
|
||||||
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){
|
|
||||||
|
|
||||||
return false;
|
if (results.RowCount() == 0)
|
||||||
}
|
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
|
||||||
|
|
||||||
return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec));
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
int ccnt = 0;
|
||||||
|
|
||||||
|
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
|
||||||
|
const ItemInst* inst = container->GetItem(x);
|
||||||
|
if(!inst)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const Item_Struct* item = GetItem(inst->GetItem()->ID);
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(item->ID == atoi(row[0]))
|
||||||
|
ccnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ccnt != atoi(row[1]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
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 "
|
||||||
|
"FROM tradeskill_recipe AS tr "
|
||||||
|
"INNER 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 "
|
||||||
|
"WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
|
||||||
|
"GROUP BY tr.id",
|
||||||
|
char_id, (unsigned long)recipe_id, containers.c_str());
|
||||||
|
auto results = QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query.c_str());
|
||||||
|
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded,"
|
if(results.RowCount() != 1)
|
||||||
" tr.trivial, tr.nofail, tr.replace_container, tr.name, tr.must_learn, tr.quest, crl.madecount"
|
return false;//just not found i guess..
|
||||||
" FROM tradeskill_recipe AS tr inner 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 "
|
|
||||||
" WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
|
|
||||||
" GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers);
|
|
||||||
|
|
||||||
if (!RunQuery(query, qlen, errbuf, &result)) {
|
auto row = results.begin();
|
||||||
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query);
|
spec->tradeskill = (SkillUseTypes)atoi(row[1]);
|
||||||
safe_delete_array(query);
|
spec->skill_needed = (int16)atoi(row[2]);
|
||||||
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
|
spec->trivial = (uint16)atoi(row[3]);
|
||||||
return(false);
|
spec->nofail = atoi(row[4]) ? true : 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);
|
|
||||||
spec->tradeskill = (SkillUseTypes)atoi(row[1]);
|
|
||||||
spec->skill_needed = (int16)atoi(row[2]);
|
|
||||||
spec->trivial = (uint16)atoi(row[3]);
|
|
||||||
spec->nofail = atoi(row[4]) ? true : false;
|
|
||||||
spec->replace_container = atoi(row[5]) ? true : false;
|
spec->replace_container = atoi(row[5]) ? true : false;
|
||||||
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);
|
|
||||||
|
|
||||||
|
spec->onfail.clear();
|
||||||
//Pull the on-fail items...
|
//Pull the on-fail items...
|
||||||
qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries"
|
query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries "
|
||||||
" WHERE failcount>0 AND recipe_id=%u", recipe_id);
|
"WHERE failcount > 0 AND recipe_id = %u", recipe_id);
|
||||||
|
results = QueryDatabase(query);
|
||||||
spec->onfail.clear();
|
if (results.Success())
|
||||||
if (RunQuery(query, qlen, errbuf, &result)) {
|
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
|
||||||
qcount = mysql_num_rows(result);
|
|
||||||
uint8 r;
|
|
||||||
for(r = 0; r < qcount; r++) {
|
|
||||||
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->onfail.push_back(std::pair<uint32,uint8>(item, num));
|
spec->onfail.push_back(std::pair<uint32,uint8>(item, num));
|
||||||
}
|
}
|
||||||
mysql_free_result(result);
|
|
||||||
}
|
spec->salvage.clear();
|
||||||
|
|
||||||
|
// Don't bother with the query if TS is nofail
|
||||||
|
if (spec->nofail)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Pull the salvage list
|
// Pull the salvage list
|
||||||
qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id);
|
query = StringFormat("SELECT item_id, salvagecount "
|
||||||
|
"FROM tradeskill_recipe_entries "
|
||||||
spec->salvage.clear();
|
"WHERE salvagecount > 0 AND recipe_id = %u", recipe_id);
|
||||||
// Don't bother with the query if TS is nofail
|
results = QueryDatabase(query);
|
||||||
if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) {
|
if (results.Success())
|
||||||
qcount = mysql_num_rows(result);
|
for(auto row = results.begin(); row != results.begin(); ++row) {
|
||||||
uint8 r;
|
|
||||||
for(r = 0; r < qcount; r++) {
|
|
||||||
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->salvage.push_back(std::pair<uint32,uint8>(item, num));
|
spec->salvage.push_back(std::pair<uint32,uint8>(item, num));
|
||||||
}
|
}
|
||||||
mysql_free_result(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
safe_delete_array(query);
|
return true;
|
||||||
|
|
||||||
return(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount)
|
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;
|
"SET recipe_id = %u, char_id = %u, madecount = %u "
|
||||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
"ON DUPLICATE KEY UPDATE madecount = %u;",
|
||||||
|
recipe_id, char_id, madeCount, madeCount);
|
||||||
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
|
auto results = QueryDatabase(query);
|
||||||
" SET recipe_id = %u, char_id = %u, madecount = %u "
|
if (!results.Success())
|
||||||
" ON DUPLICATE KEY UPDATE madecount = %u;"
|
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
, recipe_id, char_id, madecount, madecount);
|
|
||||||
|
|
||||||
if (!RunQuery(query, qlen, errbuf)) {
|
|
||||||
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query, errbuf);
|
|
||||||
}
|
|
||||||
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;
|
"ON tr.id = crl.recipe_id "
|
||||||
MYSQL_ROW row;
|
"WHERE tr.id = %u ;", CharacterID(), recipeID);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
qlen = MakeAnyLenString(&query, "SELECT tr.name, crl.madecount "
|
if (!results.Success()) {
|
||||||
" FROM tradeskill_recipe as tr "
|
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl "
|
|
||||||
" ON tr.id = crl.recipe_id "
|
|
||||||
" WHERE tr.id = %u ;", CharacterID(), recipeID);
|
|
||||||
|
|
||||||
if (!database.RunQuery(query, qlen, errbuf, &result)) {
|
|
||||||
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query, errbuf);
|
|
||||||
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 != nullptr && row[0] != nullptr) {
|
if (row[0] == nullptr)
|
||||||
// Only give Learn message if character doesn't know the recipe
|
return;
|
||||||
if (row[1] == nullptr) {
|
|
||||||
Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]);
|
|
||||||
// Actually learn the recipe now
|
|
||||||
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
|
|
||||||
" SET recipe_id = %u, char_id = %u, madecount = 0 "
|
|
||||||
" ON DUPLICATE KEY UPDATE madecount = madecount;"
|
|
||||||
, recipeID, CharacterID());
|
|
||||||
|
|
||||||
if (!database.RunQuery(query, qlen, errbuf)) {
|
// Only give Learn message if character doesn't know the recipe
|
||||||
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf);
|
if (row[1] != nullptr)
|
||||||
}
|
return;
|
||||||
safe_delete_array(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_free_result(result);
|
Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]);
|
||||||
|
// Actually learn the recipe now
|
||||||
|
query = StringFormat("INSERT INTO char_recipe_list "
|
||||||
|
"SET recipe_id = %u, char_id = %u, madecount = 0 "
|
||||||
|
"ON DUPLICATE KEY UPDATE madecount = madecount;",
|
||||||
|
recipeID, CharacterID());
|
||||||
|
results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success())
|
||||||
|
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user