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

@ -314,7 +314,7 @@ public:
/* New PP Save Functions */
bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); }
bool SaveAA();
inline bool ClientDataLoaded() const { return client_data_loaded; }
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); }
@ -664,7 +664,7 @@ public:
void IncreaseLanguageSkill(int skill_id, int value = 1);
virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; }
uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; }
bool HasSkill(SkillUseTypes skill_id) const;
bool HasSkill(SkillUseTypes skill_id) const;
bool CanHaveSkill(SkillUseTypes skill_id) const;
void SetSkill(SkillUseTypes skill_num, uint16 value);
void AddSkill(SkillUseTypes skillid, uint16 value);
@ -681,7 +681,7 @@ public:
inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
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);
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill);
@ -1452,7 +1452,7 @@ private:
unsigned int RestRegenHP;
unsigned int RestRegenMana;
unsigned int RestRegenEndurance;
bool EngagedRaidTarget;
uint32 SavedRaidRestTimer;

View File

@ -523,7 +523,7 @@ void Client::CompleteConnect()
switch (rank) {
case 0: { rank = 5; break; } // GUILD_MEMBER 0
case 1: { rank = 3; break; } // GUILD_OFFICER 1
case 2: { rank = 1; break; } // GUILD_LEADER 2
case 2: { rank = 1; break; } // GUILD_LEADER 2
default: { break; } // GUILD_NONE
}
}
@ -848,7 +848,7 @@ void Client::CompleteConnect()
void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
//ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request.
switch (CheatType)
{
case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems.
@ -1304,7 +1304,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if(strlen(cze->char_name) > 63)
return;
conn_state = ReceivedZoneEntry;
conn_state = ReceivedZoneEntry;
ClientVersion = Connection()->ClientVersion();
if (ClientVersion != EQClientUnknown)
@ -1329,7 +1329,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
client_state = CLIENT_KICKED;
return;
}
strcpy(name, cze->char_name);
/* Check for Client Spoofing */
if (client != 0) {
@ -1342,7 +1342,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
client->Disconnect();
}
uint32 pplen = 0;
uint32 pplen = 0;
EQApplicationPacket* outapp = 0;
MYSQL_RES* result = 0;
bool loaditems = 0;
@ -1366,8 +1366,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); }
else{ lsaccountid = 0; }
gmspeed = atoi(row[3]);
revoked = atoi(row[4]);
gmhideme = atoi(row[5]);
revoked = atoi(row[4]);
gmhideme = atoi(row[5]);
if (account_creation){ account_creation = atoul(row[6]); }
}
@ -1375,17 +1375,17 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid);
results = database.QueryDatabase(query);
for (auto row = results.begin(); row != results.end(); ++row) {
if (row[4] && atoi(row[4]) > 0){
guild_id = atoi(row[4]);
if (row[4] && atoi(row[4]) > 0){
guild_id = atoi(row[4]);
if (row[5] != nullptr){ guildrank = atoi(row[5]); }
else{ guildrank = GUILD_RANK_NONE; }
}
if (LFP){ LFP = atoi(row[0]); }
if (LFG){ LFG = atoi(row[1]); }
if (firstlogon){ firstlogon = atoi(row[3]); }
}
if (RuleB(Character, SharedBankPlat))
m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID());
@ -1417,7 +1417,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Set Con State for Reporting */
conn_state = PlayerProfileLoaded;
m_pp.zone_id = zone->GetZoneID();
m_pp.zone_id = zone->GetZoneID();
m_pp.zoneInstance = zone->GetInstanceID();
/* Set Total Seconds Played */
@ -1426,7 +1426,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
max_AAXP = RuleI(AA, ExpPerPoint);
/* If we can maintain intoxication across zones, check for it */
if (!RuleB(Character, MaintainIntoxicationAcrossZones))
m_pp.intoxication = 0;
m_pp.intoxication = 0;
strcpy(name, m_pp.name);
strcpy(lastname, m_pp.last_name);
@ -1435,14 +1435,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
m_pp.x = zone->safe_x();
m_pp.y = zone->safe_y();
m_pp.z = zone->safe_z();
}
}
/* If too far below ground, then fix */
// float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z);
// if (m_pp.z < (ground_z - 500))
// m_pp.z = ground_z;
/* Set Mob variables for spawn */
class_ = m_pp.class_;
class_ = m_pp.class_;
level = m_pp.level;
x_pos = m_pp.x;
y_pos = m_pp.y;
@ -1471,7 +1471,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Load Guild */
if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; }
else {
m_pp.guild_id = GuildID();
m_pp.guild_id = GuildID();
if (zone->GetZoneID() == RuleI(World, GuildBankZoneID))
GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID()));
}
@ -1711,12 +1711,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
#endif
/* Reset to max so they dont drown on zone in if its underwater */
m_pp.air_remaining = 60;
m_pp.air_remaining = 60;
/* Check for PVP Zone status*/
if (zone->IsPVPZone())
m_pp.pvp = 1;
/* Time entitled on Account: Move to account */
m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440;
m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440;
/* Reset rest timer if the durations have been lowered in the database */
if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate)))
m_pp.RestTimer = 0;
@ -1836,7 +1836,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
QueuePacket(outapp);
safe_delete(outapp);
SetAttackTimer();
SetAttackTimer();
conn_state = ZoneInfoSent;
return;
@ -11311,56 +11311,46 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
// some_id = 0 if world combiner, item number otherwise
// make where clause segment for container(s)
char containers[30];
if (tsf->some_id == 0) {
// world combiner so no item number
snprintf(containers, 29, "= %u", tsf->object_type);
}
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 containers;
if (tsf->some_id == 0)
containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number
else
containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory
std::string favoriteIDs; //gotta be big enough for 500 IDs
bool first = true;
uint16 r;
char *pos = buf;
//Assumes item IDs are <10 characters long
for (r = 0; r < 500; r++) {
if (tsf->favorite_recipes[r] == 0)
for (uint16 favoriteIndex = 0; favoriteIndex < 500; ++favoriteIndex) {
if (tsf->favorite_recipes[favoriteIndex] == 0)
continue;
if (first) {
pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]);
favoriteIDs += StringFormat("%u", tsf->favorite_recipes[favoriteIndex]);
first = false;
}
else {
pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]);
}
else
favoriteIDs += StringFormat(",%u", tsf->favorite_recipes[favoriteIndex]);
}
if (first) //no favorites....
return;
//To be a good kid, I should move this SQL somewhere else...
//but im lazy right now, so it stays here
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 "
" 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 "
" 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)) "
" GROUP BY tr.id "
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
" LIMIT 100 ", CharacterID(), buf, containers);
const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
"FROM tradeskill_recipe AS tr "
"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 "
"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)) "
"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);
safe_delete_array(query);
TradeskillSearchResults(query, tsf->object_type, tsf->some_id);
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);
}
char *query = 0;
char searchclause[140]; //2X rss->query + SQL crap
std::string searchClause;
//omit the rlike clause if query is empty
if (rss->query[0] != 0) {
char buf[120]; //larger than 2X rss->query
database.DoEscapeString(buf, rss->query, strlen(rss->query));
snprintf(searchclause, 139, "name rlike '%s' AND", buf);
searchClause = StringFormat("name rlike '%s' AND", buf);
}
else {
searchclause[0] = '\0';
}
uint32 qlen = 0;
//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 "
" FROM tradeskill_recipe AS tr "
" 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 "
" 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)) "
" GROUP BY tr.id "
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
" LIMIT 200 "
, CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers);
TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id);
safe_delete_array(query);
const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
"FROM tradeskill_recipe AS tr "
"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 "
"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)) "
"GROUP BY tr.id "
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
"LIMIT 200 ",
CharacterID(), searchClause.c_str(),
rss->mintrivial, rss->maxtrivial, containers);
TradeskillSearchResults(query, rss->object_type, rss->some_id);
return;
}

View File

@ -142,7 +142,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
// Adding augment
if (in_augment->augment_slot == -1)
{
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) &&
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) &&
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
{
tobe_auged->PutAugment(slot, *auged_with);
@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
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
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);
//pull the list of components
std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
"FROM tradeskill_recipe_entries AS tre "
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
rac->recipe_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned");
user->QueuePacket(outapp);
safe_delete(outapp);
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);
safe_delete(outapp);
return;
@ -466,17 +456,15 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
uint8 counts[10];
memset(counts, 0, sizeof(counts));
//search for all the items in their inventory
Inventory& user_inv = user->GetInv();
uint8 count = 0;
uint8 needcount = 0;
uint8 r,k;
std::list<int> MissingItems;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
uint8 needItemIndex = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++needItemIndex) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
@ -491,10 +479,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
MissingItems.push_back(item);
//dont start deleting anything until we have found it all.
items[r] = item;
counts[r] = num;
items[needItemIndex] = item;
counts[needItemIndex] = num;
}
mysql_free_result(result);
//make sure we found it all...
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...
int16 slot;
for(r = 0; r < qcount; r++) {
for(uint8 r = 0; r < results.RowCount(); r++) {
if(items[r] == 0 || counts[r] == 0)
continue; //skip empties, could prolly break here
//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);
if (slot == INVALID_INDEX) {
//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);
if (inst && !inst->IsStackable())
{
user->DeleteItemInInventory(slot, 0, true);
}
else
{
user->DeleteItemInInventory(slot, 1, true);
}
}
}
//otherwise, we found it all...
outp->reply_code = 0x00000000; //success for finding it...
user->QueuePacket(outapp);
safe_delete(outapp);
@ -654,34 +636,26 @@ SkillUseTypes Object::TypeToSkill(uint32 type)
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];
MYSQL_RES *result;
MYSQL_ROW row;
if (!database.RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
uint8 qcount = 0;
if(results.RowCount() < 1)
return; //search gave no results... not an error
qcount = mysql_num_rows(result);
if(qcount < 1) {
//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);
if(results.ColumnCount() != 6) {
LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str());
return;
}
uint8 r;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
for(auto row = results.begin(); row != results.end(); ++row) {
if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr)
continue;
uint32 recipe = (uint32)atoi(row[0]);
const char *name = row[1];
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
// Recipes that have either been made before or were
// explicitly learned are excempt from that limit
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) {
if (((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
&& row[4] == nullptr)
{
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)
&& ((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
&& row[4] == nullptr)
continue;
}
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct));
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));
FastQueuePacket(&outapp);
}
mysql_free_result(result);
}
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
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);
//pull the list of components
std::string query = StringFormat("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);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned");
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;
}
@ -773,20 +734,18 @@ void Client::SendTradeskillDetails(uint32 recipe_id) {
uint32 len;
uint32 datalen = 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
//items table, which the left join will make nullptr...
if(row[2] == nullptr || row[3] == nullptr) {
if(row[2] == nullptr || row[3] == nullptr)
continue;
}
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
uint32 icon = (uint32) atoi(row[2]);
const char *name = row[3];
len = strlen(name);
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
uint8 dist = sizeof(uint32) * (10 - count);
@ -1094,7 +1052,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
++itr;
}
return(true);
}
}
/* Tradeskill Fail */
else {
success_modifier = 2; // Halves the chance
@ -1172,7 +1130,7 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
chance_stage2 = 12.5 - (.08 * (current_raw_skill - 175));
}
}
chance_stage2 = mod_tradeskill_skillup(chance_stage2);
if (chance_stage2 > MakeRandomFloat(0, 99)) {
@ -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,
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;
uint32 count = 0;
uint32 qcount = 0;
uint32 qlen = 0;
// 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';
std::string containers;// make where clause segment for container(s)
if (some_id == 0)
containers = StringFormat("= %u", c_type); // world combiner so no item number
else
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
//Could prolly watch for stacks in this loop and handle them properly...
//just increment sum and count accordingly
bool first = true;
uint8 i;
char *pos = buf2;
for (i = 0; i < 10; i++) { // <watch> TODO: need to determine if this is bound to world/item container size
std::string buf2;
uint32 count = 0;
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);
if (inst) {
const Item_Struct* item = GetItem(inst->GetItem()->ID);
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 (!inst)
continue;
if(count < 1) {
return(false); //no items == no recipe
const Item_Struct* item = GetItem(inst->GetItem()->ID);
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 "
" FROM tradeskill_recipe_entries AS tre"
" 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(count == 0)
return false; //no items == no recipe
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
safe_delete_array(query);
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
return(false);
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount > 1) {
//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;
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);
std::string query = StringFormat("SELECT tre.recipe_id "
"FROM tradeskill_recipe_entries AS tre "
"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.c_str(), containers.c_str(), count, sum);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str());
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str());
return false;
}
if(qcount < 1)
return(false);
if (results.RowCount() > 1) {
//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.
uint32 containerId = 0;
if(some_id) { //Standard container
if(some_id) //Standard container
containerId = some_id;
}
else if(c_type) { //World container
else if(c_type)//World container
containerId = c_type;
}
else { //Invalid container
return(false);
else //Invalid container
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)"
" 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
if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container
LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!");
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);
return false;
}
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]);
mysql_free_result(result);
//Right here we verify that we actually have ALL of the tradeskill components..
//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
bool has_components = true;
char TSerrbuf[MYSQL_ERRMSG_SIZE];
char *TSquery = 0;
MYSQL_RES *TSresult;
MYSQL_ROW TSrow;
if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) {
while((TSrow = mysql_fetch_row(TSresult))!=nullptr) {
int ccnt = 0;
for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) {
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){
query = StringFormat("SELECT item_id, componentcount "
"FROM tradeskill_recipe_entries "
"WHERE recipe_id = %i AND componentcount > 0",
recipe_id);
results = QueryDatabase(query);
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);
}
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,
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)
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);
std::string containers;
if (some_id == 0)
containers = StringFormat("= %u", c_type); // world combiner so no item number
else
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
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,"
" 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);
if(results.RowCount() != 1)
return false;//just not found i guess..
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, 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) {
//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;
auto row = results.begin();
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->name = row[6];
spec->must_learn = (uint8)atoi(row[7]);
spec->quest = atoi(row[8]) ? true : false;
if (row[9] == nullptr) {
spec->has_learnt = false;
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->recipe_id = recipe_id;
mysql_free_result(result);
//Pull the on-success items...
qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries"
" WHERE successcount>0 AND recipe_id=%u", recipe_id);
if (!RunQuery(query, qlen, errbuf, &result)) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf);
safe_delete_array(query);
return(false);
query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries "
"WHERE successcount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
qcount = mysql_num_rows(result);
if(qcount < 1) {
if(results.RowCount() < 1) {
LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned");
return(false);
return false;
}
uint8 r;
spec->onsuccess.clear();
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
for(auto row = results.begin(); row != results.end(); ++row) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
spec->onsuccess.push_back(std::pair<uint32,uint8>(item, num));
}
mysql_free_result(result);
spec->onfail.clear();
//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();
if (RunQuery(query, qlen, errbuf, &result)) {
qcount = mysql_num_rows(result);
uint8 r;
for(r = 0; r < qcount; r++) {
row = mysql_fetch_row(result);
query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries "
"WHERE failcount > 0 AND recipe_id = %u", recipe_id);
results = QueryDatabase(query);
if (results.Success())
for(auto row = results.begin(); row != results.end(); ++row) {
uint32 item = (uint32)atoi(row[0]);
uint8 num = (uint8) atoi(row[1]);
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
qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id);
spec->salvage.clear();
// Don't bother with the query if TS is nofail
if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) {
qcount = mysql_num_rows(result);
uint8 r;
for(r = 0; r < qcount; r++) {
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]);
uint8 num = (uint8)atoi(row[1]);
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;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list "
" SET recipe_id = %u, char_id = %u, madecount = %u "
" ON DUPLICATE KEY UPDATE madecount = %u;"
, 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);
std::string query = StringFormat("INSERT INTO char_recipe_list "
"SET recipe_id = %u, char_id = %u, madecount = %u "
"ON DUPLICATE KEY UPDATE madecount = %u;",
recipe_id, char_id, madeCount, madeCount);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
void Client::LearnRecipe(uint32 recipeID)
{
char *query = 0;
uint32 qlen;
uint32 qcount = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
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 "
" 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);
std::string query = StringFormat("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 "
"WHERE tr.id = %u ;", CharacterID(), recipeID);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
qcount = mysql_num_rows(result);
if (qcount != 1) {
LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, qcount);
mysql_free_result(result);
safe_delete_array(query);
if (results.RowCount() != 1) {
LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount());
return;
}
safe_delete_array(query);
row = mysql_fetch_row(result);
auto row = results.begin();
if (row != nullptr && row[0] != nullptr) {
// Only give Learn message if character doesn't know the recipe
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 (row[0] == nullptr)
return;
if (!database.RunQuery(query, qlen, errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf);
}
safe_delete_array(query);
}
}
// Only give Learn message if character doesn't know the recipe
if (row[1] != nullptr)
return;
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)
{
char *query = 0;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
uint32 affected_rows = 0;
std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 1 "
"WHERE id = %u;", recipe_id);
auto results = QueryDatabase(query);
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);
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);
return results.RowsAffected() > 0;
}
bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
{
char *query = 0;
uint32 qlen;
char errbuf[MYSQL_ERRMSG_SIZE];
uint32 affected_rows = 0;
std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 0 "
"WHERE id = %u;", recipe_id);
auto results = QueryDatabase(query);
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);
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);
return results.RowsAffected() > 0;
}