diff --git a/README.md b/README.md index f2245dcf4..0c973ed43 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Further instructions on building the source can be found on the Bug reports --- -Please use the [issue tracker](issue-tracker) provided by GitHub to send us bug +Please use the [issue tracker](https://github.com/EQEmu/Server/issues) provided by GitHub to send us bug reports or feature requests. The [EQEmu Forums](http://www.eqemulator.org/forums/) also have forums to submit diff --git a/changelog.txt b/changelog.txt index 16d63a05e..556069f21 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 08/16/2014 == +KLS: (addmoreice) Trying out some unstable DB changes. Do backup your database before trying them as master will be considered unstable for a few days at least. +Uleat (Noudness): Fixed a floating-point comparison error that led to the notorious 'client bounce' (this is not related to the +'mob falling' due to not having maps installed.) This fix also eliminates a sizeable amount of unnecessary out-going packets due +to invalid orientation corrections. +Note: This patch is probably of significant enough importance that admins may wish to manually apply this to older server builds. +The number of packets reduced per second should be approximately (num_stationary_close_clients * (num_close_clients - 1)). This will +likely have the most effect in zones like: Nexus, Bazaar, Guilds (Halls) and RAID INSTANCES - where players don't move around a lot. + +== 08/15/2014 == +Uleat: Reactivated the Bot::Spawn() code for sending post-spawn wear change updates..temporary until I can sort out the proper usage. + +== 08/13/2014 == +Uleat (Kingly_Krab): Fix for bot chest armor graphic glitch. (fix also caused RoF #wc to work properly) + == 08/02/2014 == Kayen: Implemented spell_news fields - npc_no_los (check if LOS is required for spells) diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index d902bc98e..0bbbaa3fd 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -175,14 +175,14 @@ void ImportSkillCaps(SharedDatabase *db) { continue; } - std::string sql; + int class_id, skill_id, level, cap; class_id = atoi(split[0].c_str()); skill_id = atoi(split[1].c_str()); level = atoi(split[2].c_str()); cap = atoi(split[3].c_str()); - StringFormat(sql, "INSERT INTO skill_caps(class, skillID, level, cap) VALUES(%d, %d, %d, %d)", + std::string sql = StringFormat("INSERT INTO skill_caps(class, skillID, level, cap) VALUES(%d, %d, %d, %d)", class_id, skill_id, level, cap); db->RunQuery(sql.c_str(), (uint32)sql.length()); @@ -226,7 +226,7 @@ void ImportBaseData(SharedDatabase *db) { mana_fac = atof(split[8].c_str()); end_fac = atof(split[9].c_str()); - StringFormat(sql, "INSERT INTO base_data(level, class, hp, mana, end, unk1, unk2, hp_fac, " + sql = StringFormat("INSERT INTO base_data(level, class, hp, mana, end, unk1, unk2, hp_fac, " "mana_fac, end_fac) VALUES(%d, %d, %f, %f, %f, %f, %f, %f, %f, %f)", level, class_id, hp, mana, end, unk1, unk2, hp_fac, mana_fac, end_fac); diff --git a/common/Item.cpp b/common/Item.cpp index 3bdd33866..ff02d1574 100644 --- a/common/Item.cpp +++ b/common/Item.cpp @@ -916,8 +916,7 @@ void Inventory::dumpItemCollection(const std::map &collection) if (!inst || !inst->GetItem()) continue; - std::string slot; - StringFormat(slot, "Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); + std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); std::cout << slot << std::endl; dumpBagContents(inst, &it); @@ -936,8 +935,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { if (!baginst || !baginst->GetItem()) continue; - std::string subSlot; - StringFormat(subSlot, " Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first), + std::string subSlot = StringFormat(" Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first), baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges()); std::cout << subSlot << std::endl; } diff --git a/common/MiscFunctions.h b/common/MiscFunctions.h index 2117773f6..c057af8d8 100644 --- a/common/MiscFunctions.h +++ b/common/MiscFunctions.h @@ -103,7 +103,8 @@ int NewFloatToEQ13(float d); int FloatToEQ19(float d); int FloatToEQH(float d); - +// macro to catch fp errors (provided by noudness) +#define FCMP(a,b) (fabs(a-b) < FLT_EPSILON) #define _ITOA_BUFLEN 25 const char *itoa(int num); //not thread safe diff --git a/common/MySQLRequestResult.cpp b/common/MySQLRequestResult.cpp index 56e62ae37..f364bf9b2 100644 --- a/common/MySQLRequestResult.cpp +++ b/common/MySQLRequestResult.cpp @@ -10,35 +10,24 @@ MySQLRequestResult::MySQLRequestResult() MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) : m_CurrentRow(result), m_OneBeyondRow() { - if (errorBuffer != nullptr) - m_Success = false; - else if (errorBuffer == nullptr && result == nullptr) - { - m_Success = false; - -#ifdef _EQDEBUG - std::cout << "DB Query Error: No Result" << std::endl; -#endif - m_ErrorNumber = UINT_MAX; - m_ErrorBuffer = new char[MYSQL_ERRMSG_SIZE]; - - strcpy(m_ErrorBuffer, "DBcore::RunQuery: No Result"); - } - else - m_Success = true; - - m_Result = result; - m_ErrorBuffer = errorBuffer; + m_Result = result; m_RowsAffected = rowsAffected; m_RowCount = rowCount; m_ColumnCount = columnCount; - // If we actually need the column length / fields it will be - // requested at that time, no need to pull it in just to cache it. - // Normal usage would have it as nullptr most likely anyways. - m_ColumnLengths = nullptr; - m_Fields = nullptr; m_LastInsertedID = lastInsertedID; - m_ErrorNumber = errorNumber; + + // If we actually need the column length / fields it will be + // requested at that time, no need to pull it in just to cache it. + // Normal usage would have it as nullptr most likely anyways. + m_ColumnLengths = nullptr; + m_Fields = nullptr; + + if (errorBuffer != nullptr) + m_Success = false; + + m_Success = true; + m_ErrorNumber = errorNumber; + m_ErrorBuffer = errorBuffer; } void MySQLRequestResult::FreeInternals() @@ -113,7 +102,7 @@ MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem) m_ColumnLengths = moveItem.m_ColumnLengths; m_Fields = moveItem.m_Fields; - // Keeps deconstructor from double freeing + // Keeps deconstructor from double freeing // pre move instance. moveItem.ZeroOut(); } @@ -140,8 +129,8 @@ MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other) m_ColumnLengths = other.m_ColumnLengths; m_Fields = other.m_Fields; - // Keeps deconstructor from double freeing + // Keeps deconstructor from double freeing // pre move instance. other.ZeroOut(); return *this; -} \ No newline at end of file +} diff --git a/common/MySQLRequestRow.cpp b/common/MySQLRequestRow.cpp index 46bdaffd9..f8c2b4c06 100644 --- a/common/MySQLRequestRow.cpp +++ b/common/MySQLRequestRow.cpp @@ -1,6 +1,6 @@ #include "MySQLRequestRow.h" -MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row) +MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row) : m_Result(row.m_Result), m_MySQLRow(row.m_MySQLRow) { } @@ -26,13 +26,17 @@ MySQLRequestRow& MySQLRequestRow::operator=(MySQLRequestRow& moveItem) moveItem.m_Result = nullptr; moveItem.m_MySQLRow = nullptr; - + return *this; } MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result) - : m_Result(result), m_MySQLRow(mysql_fetch_row(m_Result)) + : m_Result(result) { + if (result != nullptr) + m_MySQLRow = mysql_fetch_row(result); + else + m_MySQLRow = nullptr; } MySQLRequestRow& MySQLRequestRow::operator++() @@ -41,19 +45,19 @@ MySQLRequestRow& MySQLRequestRow::operator++() return *this; } -MySQLRequestRow MySQLRequestRow::operator++(int) +MySQLRequestRow MySQLRequestRow::operator++(int) { - MySQLRequestRow tmp(*this); - operator++(); + MySQLRequestRow tmp(*this); + operator++(); return tmp; } -bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs) +bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs) { return m_MySQLRow == rhs.m_MySQLRow; } -bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs) +bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs) { return m_MySQLRow != rhs.m_MySQLRow; } diff --git a/common/StringUtil.cpp b/common/StringUtil.cpp index 71bb961e1..80c92fa88 100644 --- a/common/StringUtil.cpp +++ b/common/StringUtil.cpp @@ -38,8 +38,9 @@ // original source: // https://github.com/facebook/folly/blob/master/folly/String.cpp // -void vStringFormat(std::string& output, const char* format, va_list args) +const std::string vStringFormat(const char* format, va_list args) { + std::string output; va_list tmpargs; va_copy(tmpargs,args); @@ -48,11 +49,8 @@ void vStringFormat(std::string& output, const char* format, va_list args) if (characters_used < 0) { // Looks like we have an invalid format string. - // error out. - std::string errorMessage("Invalid format string; snprintf returned negative with format string: "); - errorMessage.append(format); - - throw std::runtime_error(errorMessage); + // return empty string. + return ""; } else if ((unsigned int)characters_used > output.capacity()) { output.resize(characters_used+1); @@ -62,12 +60,10 @@ void vStringFormat(std::string& output, const char* format, va_list args) if (characters_used < 0) { // We shouldn't have a format error by this point, but I can't imagine what error we - // could have by this point. Still, error out and report it. - std::string errorMessage("Invalid format string or unknown vsnprintf error; vsnprintf returned negative with format string: "); - errorMessage.append(format); - - throw std::runtime_error(errorMessage); + // could have by this point. Still, return empty string; + return ""; } + return std::move(output); } else { output.resize(characters_used + 1); @@ -78,24 +74,23 @@ void vStringFormat(std::string& output, const char* format, va_list args) if (characters_used < 0) { // We shouldn't have a format error by this point, but I can't imagine what error we - // could have by this point. still error out and report it. - std::string errorMessage("Invalid format string or unknown vsnprintf error; vsnprintf returned negative with format string: "); - errorMessage.append(format); - - throw std::runtime_error(errorMessage); + // could have by this point. Still, return empty string; + return ""; } - + return std::move(output); } } -void StringFormat(std::string& output, const char* format, ...) +const std::string StringFormat(const char* format, ...) { va_list args; va_start(args, format); - vStringFormat(output,format,args); + std::string output = vStringFormat(format,args); va_end(args); + return std::move(output); } + // normal strncpy doesnt put a null term on copied strings, this one does // ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp char* strn0cpy(char* dest, const char* source, uint32 size) { @@ -384,3 +379,15 @@ std::string EscapeString(const std::string &s) { return ret; } + +bool isAlphaNumeric(const char *text) +{ + for (unsigned int charIndex=0; charIndex 'z') && + (text[charIndex] < 'A' || text[charIndex] > 'Z') && + (text[charIndex] < '0' || text[charIndex] > '9')) + return false; + } + + return true; +} \ No newline at end of file diff --git a/common/StringUtil.h b/common/StringUtil.h index 46ff43183..fe7e83edf 100644 --- a/common/StringUtil.h +++ b/common/StringUtil.h @@ -19,11 +19,12 @@ #include #include #include +#include #include "types.h" -void vStringFormat(std::string& output, const char* format, va_list args); -void StringFormat(std::string& output, const char* format, ...); +const std::string vStringFormat(const char* format, va_list args); +const std::string StringFormat(const char* format, ...); std::string EscapeString(const std::string &s); const char *MakeLowerString(const char *source); @@ -51,4 +52,6 @@ char *RemoveApostrophes(const char *s); std::vector SplitString(const std::string &s, char delim); +bool isAlphaNumeric(const char *text); + #endif diff --git a/common/database.cpp b/common/database.cpp index 6fdf6b1af..2d5bdbdcc 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -43,15 +43,10 @@ #include "database.h" #include "eq_packet_structs.h" #include "guilds.h" -//#include "MiscFunctions.h" #include "StringUtil.h" #include "extprofile.h" extern Client client; -/* -This is the amount of time in seconds the client has to enter the zone -server after the world server, or inbetween zones when that is finished -*/ /* Establish a connection to a mysql database with the supplied parameters @@ -100,8 +95,6 @@ void Database::DBInitVars() { varcache_lastupdate = 0; } - - void Database::HandleMysqlError(uint32 errnum) { /* switch(errnum) { case 0: @@ -141,276 +134,212 @@ Database::~Database() } } - /* Check if there is an account with name "name" and password "password" Return the account id or zero if no account matches. Zero will also be returned if there is a database error. */ uint32 Database::CheckLogin(const char* name, const char* password, int16* oStatus) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; if(strlen(name) >= 50 || strlen(password) >= 50) return(0); char tmpUN[100]; char tmpPW[100]; + DoEscapeString(tmpUN, name, strlen(name)); DoEscapeString(tmpPW, password, strlen(password)); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT id, status FROM account WHERE name='%s' AND password is not null " + std::string query = StringFormat("SELECT id, status FROM account WHERE name='%s' AND password is not null " "and length(password) > 0 and (password='%s' or password=MD5('%s'))", - tmpUN, tmpPW, tmpPW), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 id = atoi(row[0]); - if (oStatus) - *oStatus = atoi(row[1]); - mysql_free_result(result); - return id; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else + tmpUN, tmpPW, tmpPW); + auto results = QueryDatabase(query); + + if (!results.Success()) { - std::cerr << "Error in CheckLogin query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; + std::cerr << "Error in CheckLogin query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return 0; + auto row = results.begin(); + + uint32 id = atoi(row[0]); + + if (oStatus) + *oStatus = atoi(row[1]); + + return id; } - -//Lieka: Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. +//Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. bool Database::CheckBannedIPs(const char* loginIP) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - //std::cout << "Checking against Banned IPs table."<< std::endl; //Lieka: Debugging - if (RunQuery(query, MakeAnyLenString(&query, "SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) - { - //std::cout << loginIP << " was present in the banned IPs table" << std::endl; //Lieka: Debugging - mysql_free_result(result); - return true; - } - else - { - //std::cout << loginIP << " was not present in the banned IPs table." << std::endl; //Lieka: Debugging - mysql_free_result(result); - return false; - } - mysql_free_result(result); - } - else + std::string query = StringFormat("SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP); + + auto results = QueryDatabase(query); + + if (!results.Success()) { - std::cerr << "Error in CheckBannedIPs query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::cerr << "Error in CheckBannedIPs query '" << query << "' " << results.ErrorMessage() << std::endl; return true; } - return true; + + if (results.RowCount() != 0) + return true; + + return false; } bool Database::AddBannedIP(char* bannedIP, const char* notes) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes), errbuf)) { - std::cerr << "Error in ReserveName query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); + return true; } - //End Lieka Edit bool Database::CheckGMIPs(const char* ip_address, uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - mysql_free_result(result); - return true; - } else { - mysql_free_result(result); - return false; - } - mysql_free_result(result); + std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); - } else { - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) return false; - } + + if (results.RowCount() == 1) + return true; return false; } bool Database::AddGMIP(char* ip_address, char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name), errbuf)) { - safe_delete_array(query); - return false; - } - safe_delete_array(query); - return true; + auto results = QueryDatabase(query); + + return results.Success(); } void Database::LoginIP(uint32 AccountID, const char* LoginIP) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP), errbuf)) { - std::cerr << "Error in Log IP query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cerr << "Error in Log IP query '" << query << "' " << results.ErrorMessage() << std::endl; } int16 Database::CheckStatus(uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" + " FROM `account` WHERE `id` = %i", account_id); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" - " FROM `account` WHERE `id` = %i", account_id), errbuf, &result)) + auto results = QueryDatabase(query); + + if (!results.Success()) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - - int16 status = atoi(row[0]); - - int32 suspendeduntil = 0; - // MariaDB initalizes with NULL if unix_timestamp() is out of range - if (row[1] != NULL) { - suspendeduntil = atoi(row[1]); - } - - int32 current = atoi(row[2]); - - mysql_free_result(result); - - if(suspendeduntil > current) - return -1; - - return status; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in CheckStatus query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; + std::cerr << "Error in CheckStatus query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + int16 status = atoi(row[0]); + + int32 suspendeduntil = 0; + + // MariaDB initalizes with NULL if unix_timestamp() is out of range + if (row[1] != nullptr) { + suspendeduntil = atoi(row[1]); + } + + int32 current = atoi(row[2]); + + if(suspendeduntil > current) + return -1; + + return status; } uint32 Database::CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 querylen; - uint32 last_insert_id; + std::string query; if (password) - querylen = MakeAnyLenString(&query, "INSERT INTO account SET name='%s', password='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name,password,status, lsaccount_id); + query = StringFormat("INSERT INTO account SET name='%s', password='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name,password,status, lsaccount_id); else - querylen = MakeAnyLenString(&query, "INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); + query = StringFormat("INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); std::cerr << "Account Attempting to be created:" << name << " " << (int16) status << std::endl; - if (!RunQuery(query, querylen, errbuf, 0, 0, &last_insert_id)) { - std::cerr << "Error in CreateAccount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - safe_delete_array(query); - if (last_insert_id == 0) { - std::cerr << "Error in CreateAccount query '" << query << "' " << errbuf << std::endl; + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return last_insert_id; + if (results.LastInsertedID() == 0) + { + std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + return results.LastInsertedID(); } bool Database::DeleteAccount(const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); - std::cerr << "Account Attempting to be deleted:" << name << std::endl; - if (RunQuery(query, MakeAnyLenString(&query, "DELETE FROM account WHERE name='%s';",name), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - return true; - } - } - else { + std::cout << "Account Attempting to be deleted:" << name << std::endl; - std::cerr << "Error in DeleteAccount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } + auto results = QueryDatabase(query); - return false; -} - -bool Database::SetLocalPassword(uint32 accid, const char* password) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET password=MD5('%s') where id=%i;", password, accid), errbuf)) { - std::cerr << "Error in SetLocalPassword query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + if (!results.Success()) + { + std::cerr << "Error in DeleteAccount query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; + } + + return results.RowsAffected() == 1; +} + +bool Database::SetLocalPassword(uint32 accid, const char* password) { + std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", password, accid); + + auto results = QueryDatabase(query); + + if (!results.Success()) { + std::cerr << "Error in SetLocalPassword query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; } bool Database::SetAccountStatus(const char* name, int16 status) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE account SET status=%i WHERE name='%s';", status, name); std::cout << "Account being GM Flagged:" << name << ", Level: " << (int16) status << std::endl; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET status=%i WHERE name='%s';", status, name), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - return false; - } - safe_delete_array(query); - if (affected_rows == 0) { + auto results = QueryDatabase(query); + + if (!results.Success()) + return false; + + if (results.RowsAffected() == 0) + { std::cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; return false; } @@ -420,15 +349,16 @@ bool Database::SetAccountStatus(const char* name, int16 status) { bool Database::ReserveName(uint32 account_id, char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name), errbuf)) { - std::cerr << "Error in ReserveName query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); + return true; } @@ -438,318 +368,202 @@ returns false on failure, true otherwise */ bool Database::DeleteCharacter(char *name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query=0; - MYSQL_RES *result; - MYSQL_ROW row; - int charid, matches; - uint32 affected_rows; + std::string query=StringFormat("SELECT id from character_ WHERE name='%s'", name); + int charid; if(!name || !strlen(name)) { - printf("DeleteCharacter: request to delete without a name (empty char slot)\n"); + std::cerr << "DeleteCharacter: request to delete without a name (empty char slot)" << std::endl; return false; } // get id from character_ before deleting record so we can clean up inventory and qglobal #if DEBUG >= 5 - printf("DeleteCharacter: Attempting to delete '%s'\n", name); + std::cout << "DeleteCharacter: Attempting to delete '" << name << "'" << std::endl; #endif - RunQuery(query, MakeAnyLenString(&query, "SELECT id from character_ WHERE name='%s'", name), errbuf, &result); - if (query) + + auto results = QueryDatabase(query); + + if(results.RowCount() != 1) { - safe_delete_array(query); - query = nullptr; - } - matches = mysql_num_rows(result); - if(matches == 1) - { - row = mysql_fetch_row(result); - charid = atoi(row[0]); -#if DEBUG >= 5 - printf("DeleteCharacter: found '%s' with char id: %d\n", name, charid); -#endif - } - else - { - printf("DeleteCharacter: error: got %d rows matching '%s'\n", matches, name); - if(result) - { - mysql_free_result(result); - result = nullptr; - } + std::cerr << "DeleteCharacter error: got " << results.RowCount() << " rows matching '" << name << "'" << std::endl; return false; } - if(result) - { - mysql_free_result(result); - result = nullptr; - } - - + auto row = results.begin(); + charid = atoi(row[0]); #if DEBUG >= 5 - printf("DeleteCharacter: deleting '%s' (id %d): ", name, charid); - printf(" quest_globals"); + std::cout << "DeleteCharacter: found '" << name << "' with char id: " << charid << std::endl; + std::cout << "DeleteCharacter: deleting << '" << name << "' (id " << charid << "): " << std::endl; + std::cout << " quest_globals"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from quest_globals WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from quest_globals WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" character_tasks"); + std::cout << " character_tasks"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from character_tasks WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from character_tasks WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" character_activities"); + std::cout << " character_activities"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from character_activities WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from character_activities WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" character_enabledtasks"); + std::cout << " character_enabledtasks"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from character_enabledtasks WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from character_enabledtasks WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" completed_tasks"); + std::cout << " completed_tasks"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from completed_tasks WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from completed_tasks WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" friends"); + std::cout << " friends"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from friends WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from friends WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" mail"); + std::cout << " mail"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from mail WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat( "DELETE from mail WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" ptimers"); + std::cout << " ptimers"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from timers WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from timers WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" inventory"); + std::cout << " inventory"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from inventory WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE from inventory WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" guild_members"); + std::cout << " guild_members"; #endif + #ifdef BOTS - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid), errbuf, nullptr, &affected_rows); + query = StringFormat("DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid); #else - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); + query = StringFormat("DELETE FROM guild_members WHERE char_id='%d'", charid); #endif - if(query) - { - safe_delete_array(query); - query = nullptr; - } + QueryDatabase(query); #if DEBUG >= 5 - printf(" recipes"); + std::cout << " recipes"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM char_recipe_list WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM char_recipe_list WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" adventure_stats"); + std::cout << " adventure_stats"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM adventure_stats WHERE player_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM adventure_stats WHERE player_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" zone_flags"); + std::cout << " zone_flags"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM zone_flags WHERE charID='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM zone_flags WHERE charID='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" titles"); + std::cout << " titles"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM titles WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM titles WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" titlesets"); + std::cout << " titlesets"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM player_titlesets WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" keyring"); + std::cout << " keyring"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM keyring WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM keyring WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" factions"); + std::cout << " factions"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM faction_values WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" instances"); + std::cout << " instances"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE charid='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM instance_list_player WHERE charid='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf(" _character"); + std::cout << " _character"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE from character_ WHERE id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) + + query = StringFormat("DELETE from character_ WHERE id='%d'", charid); + results = QueryDatabase(query); + + if(results.RowsAffected() != 1) // here we have to have a match or it's an error { - safe_delete_array(query); - query = nullptr; - } - if(affected_rows != 1) // here we have to have a match or it's an error - { - LogFile->write(EQEMuLog::Error, "DeleteCharacter: error: delete operation affected %d rows\n", affected_rows); + LogFile->write(EQEMuLog::Error, "DeleteCharacter: error: delete operation affected %d rows\n", results.RowsAffected()); return false; } #if DEBUG >= 5 - printf(" alternate currency"); + std::cout << " alternate currency"; #endif - RunQuery(query, MakeAnyLenString(&query, "DELETE FROM character_alt_currency WHERE char_id='%d'", charid), errbuf, nullptr, &affected_rows); - if(query) - { - safe_delete_array(query); - query = nullptr; - } + + query = StringFormat("DELETE FROM character_alt_currency WHERE char_id='%d'", charid); + QueryDatabase(query); #if DEBUG >= 5 - printf("\n"); + std::cout << std::endl; #endif - printf("DeleteCharacter: successfully deleted '%s' (id %d)\n", name, charid); + std::cout << "DeleteCharacter: successfully deleted '" << name << "' (id " << charid << ")" << std::endl; return true; } -// Store new character information into the character_ and inventory tables (this won't store starting augments) + +// Store new character information into the character_ and inventory tables bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext) { - char errbuf[MYSQL_ERRMSG_SIZE]; char query[256+sizeof(PlayerProfile_Struct)*2+sizeof(ExtendedProfile_Struct)*2+5]; char* end = query; - uint32 affected_rows = 0; - int i; uint32 charid = 0; - char* charidquery = 0; - char* invquery = 0; - MYSQL_RES *result; - MYSQL_ROW row = 0; char zone[50]; float x, y, z; -// memset(&playeraa, 0, sizeof(playeraa)); - - // get the char id (used in inventory inserts below) - if(!RunQuery - ( - charidquery, - MakeAnyLenString - ( - &charidquery, - "SELECT id FROM character_ where name='%s'", - pp->name - ), - errbuf, - &result - )) { - safe_delete_array(charidquery); - LogFile->write(EQEMuLog::Error, "Error in char store id query: %s: %s", charidquery, errbuf); - return(false); - } - safe_delete_array(charidquery); - - if(mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - if(row[0]) - charid = atoi(row[0]); - } + charid = GetCharacterID(pp->name); if(!charid) { @@ -764,6 +578,7 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven pp->zone_id = 1; } else strn0cpy(zone, zname, 49); + x=pp->x; y=pp->y; z=pp->z; @@ -779,46 +594,32 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); end += sprintf(end, "\' WHERE account_id=%d AND name='%s'",account_id, pp->name); - RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows); + auto results = QueryDatabase(query, (uint32) (end - query)); + // stack assigned query, no need to delete it. - if(!affected_rows) + if(!results.RowsAffected()) { - LogFile->write(EQEMuLog::Error, "StoreCharacter query '%s' %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "StoreCharacter query '%s' %s", query, results.ErrorMessage().c_str()); return false; } - affected_rows = 0; - - - // Doodman: Is this even used? // now the inventory - - for (i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::BANK_BAGS_END;) + std::string invquery; + for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) { - const ItemInst* newinv = inv->GetItem((int16)i); - if (newinv) + const ItemInst* newinv = inv->GetItem(i); + if (!newinv) { - MakeAnyLenString - ( - &invquery, - "INSERT INTO inventory SET " - "charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", - charid, i, newinv->GetItem()->ID, - newinv->GetCharges(), newinv->GetColor() - ); + invquery = StringFormat("INSERT INTO inventory SET charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", + charid, i, newinv->GetItem()->ID,newinv->GetCharges(), newinv->GetColor()); + auto results = QueryDatabase(invquery); - RunQuery(invquery, strlen(invquery), errbuf, 0, &affected_rows); - if(!affected_rows) - { - LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery, errbuf); - } + if (!results.RowsAffected()) + LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery.c_str(), results.ErrorMessage().c_str()); #if EQDEBUG >= 9 else - { - LogFile->write(EQEMuLog::Debug, "StoreCharacter inventory succeeded. Query '%s' %s", invquery, errbuf); - } + LogFile->write(EQEMuLog::Debug, "StoreCharacter inventory succeeded. Query '%s'", invquery.c_str()); #endif - safe_delete_array(invquery); } if (i == MainCursor) { @@ -854,220 +655,201 @@ the name "name" or zero if no character with that name was found Zero will also be returned if there is a database error. */ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", charname); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT account_id, id FROM character_ WHERE name='%s'", charname), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function - if (oCharID) - *oCharID = atoi(row[1]); - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + uint32 accountId = atoi(row[0]); + + if (oCharID) + *oCharID = atoi(row[1]); + + return accountId; } // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 ret = 0; + std::string query = StringFormat("SELECT account_id FROM character_ WHERE id=%i", char_id); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT account_id FROM character_ WHERE id=%i", char_id), errbuf, &result)) { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - ret = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query, errbuf); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; } - safe_delete_array(query); - return ret; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* lsid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + if (!isAlphaNumeric(accname)) + return 0; + std::string query = StringFormat("SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname); + auto results = QueryDatabase(query); - for (unsigned int i=0; i 'z') && - (accname[i] < 'A' || accname[i] > 'Z') && - (accname[i] < '0' || accname[i] > '9')) - return 0; + if (!results.Success()) + { + std::cerr << "Error in GetAccountIDByAcc query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function - if (status) - *status = atoi(row[1]); - if (lsid) { - if (row[2]) - *lsid = atoi(row[2]); - else - *lsid = 0; - } - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetAccountIDByAcc query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + uint32 id = atoi(row[0]); + + if (status) + *status = atoi(row[1]); + + if (lsid) + { + if (row[2]) + *lsid = atoi(row[2]); + else + *lsid = 0; } - return 0; + return id; } void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); + auto results = QueryDatabase(query); - strcpy(name, row[0]); - if (row[1] && oLSAccountID) { - *oLSAccountID = atoi(row[1]); - } - } - - mysql_free_result(result); + if (!results.Success()) + { + std::cerr << "Error in GetAccountName query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } - else { - safe_delete_array(query); - std::cerr << "Error in GetAccountName query '" << query << "' " << errbuf << std::endl; + + if (results.RowCount() != 1) + return; + + auto row = results.begin(); + + strcpy(name, row[0]); + if (row[1] && oLSAccountID) { + *oLSAccountID = atoi(row[1]); } + } void Database::GetCharName(uint32 char_id, char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT name FROM character_ WHERE id='%i'", char_id); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name FROM character_ WHERE id='%i'", char_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - - strcpy(name, row[0]); - } - - mysql_free_result(result); - } - else { - safe_delete_array(query); - std::cerr << "Error in GetCharName query '" << query << "' " << errbuf << std::endl; + if (!results.Success()) + { + std::cerr << "Error in GetCharName query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } + auto row = results.begin(); + strcpy(name, row[0]); } bool Database::LoadVariables() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; + char *query = nullptr; - if (RunQuery(query, LoadVariables_MQ(&query), errbuf, &result)) { + auto results = QueryDatabase(query, LoadVariables_MQ(&query)); + + if (!results.Success()) + { + std::cerr << "Error in LoadVariables query '" << query << "' " << results.ErrorMessage() << std::endl; safe_delete_array(query); - bool ret = LoadVariables_result(result); - mysql_free_result(result); - return ret; + return false; } - else { - std::cerr << "Error in LoadVariables query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return false; + + safe_delete_array(query); + return LoadVariables_result(std::move(results)); } -uint32 Database::LoadVariables_MQ(char** query) { -// the read of this single variable should be atomic... this was causing strange problems -// LockMutex lock(&Mvarcache); +uint32 Database::LoadVariables_MQ(char** query) +{ return MakeAnyLenString(query, "SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= %d", varcache_lastupdate); } -bool Database::LoadVariables_result(MYSQL_RES* result) { - uint32 i; - MYSQL_ROW row; +// always returns true? not sure about this. +bool Database::LoadVariables_result(MySQLRequestResult results) { + uint32 i = 0; LockMutex lock(&Mvarcache); - if (mysql_num_rows(result) > 0) { - if (!varcache_array) { - varcache_max = mysql_num_rows(result); - varcache_array = new VarCache_Struct*[varcache_max]; - for (i=0; ivarname, row[0]) == 0) { - delete varcache_array[i]; - varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; - strn0cpy(varcache_array[i]->varname, row[0], sizeof(varcache_array[i]->varname)); - strcpy(varcache_array[i]->value, row[1]); - break; - } - } - else { + + if (results.RowCount() == 0) + return true; + + if (!varcache_array) { + varcache_max = results.RowCount(); + varcache_array = new VarCache_Struct*[varcache_max]; + for (i=0; ivarname, row[0]) == 0) { + delete varcache_array[i]; varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; - strcpy(varcache_array[i]->varname, row[0]); + strn0cpy(varcache_array[i]->varname, row[0], sizeof(varcache_array[i]->varname)); strcpy(varcache_array[i]->value, row[1]); break; } } - } - uint32 max_used = 0; - for (i=0; i max_used) - max_used = i; + else { + varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; + strcpy(varcache_array[i]->varname, row[0]); + strcpy(varcache_array[i]->value, row[1]); + break; } } - max_used++; - varcache_max = max_used; } + + uint32 max_used = 0; + for (i=0; i max_used) + max_used = i; + } + } + + varcache_max = max_used + 1; + return true; } @@ -1094,10 +876,7 @@ bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_ } bool Database::SetVariable(const char* varname_in, const char* varvalue_in) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - + char *varname,*varvalue; varname=(char *)malloc(strlen(varname_in)*2+1); @@ -1105,291 +884,244 @@ bool Database::SetVariable(const char* varname_in, const char* varvalue_in) { DoEscapeString(varname, varname_in, strlen(varname_in)); DoEscapeString(varvalue, varvalue_in, strlen(varvalue_in)); - if (RunQuery(query, MakeAnyLenString(&query, "Update variables set value='%s' WHERE varname like '%s'", varvalue, varname), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - LoadVariables(); // refresh cache - free(varname); - free(varvalue); - return true; - } - else { - if (RunQuery(query, MakeAnyLenString(&query, "Insert Into variables (varname, value) values ('%s', '%s')", varname, varvalue), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - LoadVariables(); // refresh cache - free(varname); - free(varvalue); - return true; - } - } - } + std::string query = StringFormat("Update variables set value='%s' WHERE varname like '%s'", varvalue, varname); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in SetVariable query '" << query << "' " << results.ErrorMessage() << std::endl; + free(varname); + free(varvalue); + return false; } - else { - std::cerr << "Error in SetVariable query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + if (results.RowsAffected() == 1) + { + LoadVariables(); // refresh cache + free(varname); + free(varvalue); + return true; } + + query = StringFormat("Insert Into variables (varname, value) values ('%s', '%s')", varname, varvalue); + results = QueryDatabase(query); free(varname); free(varvalue); - return false; + + if (results.RowsAffected() != 1) + return false; + + LoadVariables(); // refresh cache + return true; } uint32 Database::GetMiniLoginAccount(char* ip){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 retid = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM account WHERE minilogin_ip='%s'", ip), errbuf, &result)) { - safe_delete_array(query); - if ((row = mysql_fetch_row(result))) - retid = atoi(row[0]); - mysql_free_result(result); - } - else + + std::string query = StringFormat("SELECT id FROM account WHERE minilogin_ip='%s'", ip); + auto results = QueryDatabase(query); + + if (!results.Success()) { - std::cerr << "Error in GetMiniLoginAccount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::cerr << "Error in GetMiniLoginAccount query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return retid; + + auto row = results.begin(); + + return atoi(row[0]); } -// Pyro: Get zone starting points from DB +// Get zone starting points from DB bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe_x, float* safe_y, float* safe_z, int16* minstatus, uint8* minlevel, char *flag_needed) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - // int buf_len = 256; - // int chars = -1; - MYSQL_RES *result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT safe_x, safe_y, safe_z, min_status, min_level, flag_needed FROM zone " + " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT safe_x, safe_y, safe_z, min_status, min_level, " - " flag_needed FROM zone " - " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - if (safe_x != 0) - *safe_x = atof(row[0]); - if (safe_y != 0) - *safe_y = atof(row[1]); - if (safe_z != 0) - *safe_z = atof(row[2]); - if (minstatus != 0) - *minstatus = atoi(row[3]); - if (minlevel != 0) - *minlevel = atoi(row[4]); - if (flag_needed != nullptr) - strcpy(flag_needed, row[5]); - mysql_free_result(result); - return true; - } - - mysql_free_result(result); - } - else + if (!results.Success()) { - std::cerr << "Error in GetSafePoint query '" << query << "' " << errbuf << std::endl; + std::cerr << "Error in GetSafePoint query '" << query << "' " << results.ErrorMessage() << std::endl; std::cerr << "If it errors, run the following querys:\n"; std::cerr << "ALTER TABLE `zone` CHANGE `minium_level` `min_level` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; std::cerr << "ALTER TABLE `zone` CHANGE `minium_status` `min_status` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; std::cerr << "ALTER TABLE `zone` ADD flag_needed VARCHAR(128) NOT NULL DEFAULT '';\n"; - - safe_delete_array(query); - } - return false; -} - - -bool Database::GetZoneLongName(const char* short_name, char** long_name, char* file_name, float* safe_x, float* safe_y, float* safe_z, uint32* graveyard_id, uint32* maxclients) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT long_name, file_name, safe_x, safe_y, safe_z, graveyard_id, maxclients FROM zone WHERE short_name='%s' AND version=0", short_name), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - if (long_name != 0) { - *long_name = strcpy(new char[strlen(row[0])+1], row[0]); - } - if (file_name != 0) { - if (row[1] == 0) - strcpy(file_name, short_name); - else - strcpy(file_name, row[1]); - } - if (safe_x != 0) - *safe_x = atof(row[2]); - if (safe_y != 0) - *safe_y = atof(row[3]); - if (safe_z != 0) - *safe_z = atof(row[4]); - if (graveyard_id != 0) - *graveyard_id = atoi(row[5]); - if (maxclients) - *maxclients = atoi(row[6]); - mysql_free_result(result); - return true; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetZoneLongName query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); return false; } - return false; -} -uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 GraveyardID = 0; + if (results.RowCount() == 0) + return false; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version), errbuf, &result)) - { - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - GraveyardID = atoi(row[0]); - } - mysql_free_result(result); - safe_delete_array(query); - return GraveyardID; + auto row = results.begin(); + + if (safe_x != nullptr) + *safe_x = atof(row[0]); + if (safe_y != nullptr) + *safe_y = atof(row[1]); + if (safe_z != nullptr) + *safe_z = atof(row[2]); + if (minstatus != nullptr) + *minstatus = atoi(row[3]); + if (minlevel != nullptr) + *minlevel = atoi(row[4]); + if (flag_needed != nullptr) + strcpy(flag_needed, row[5]); + + return true; +} + +bool Database::GetZoneLongName(const char* short_name, char** long_name, char* file_name, float* safe_x, float* safe_y, float* safe_z, uint32* graveyard_id, uint32* maxclients) { + + std::string query = StringFormat("SELECT long_name, file_name, safe_x, safe_y, safe_z, graveyard_id, maxclients FROM zone WHERE short_name='%s' AND version=0", short_name); + auto results = QueryDatabase(query); + + if (!results.Success()) { + std::cerr << "Error in GetZoneLongName query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; } - else - { - std::cerr << "Error in GetZoneGraveyardID query '" << query << "' " << errbuf << std::endl; + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + if (long_name != nullptr) + *long_name = strcpy(new char[strlen(row[0])+1], row[0]); + + if (file_name != nullptr) { + if (row[1] == nullptr) + strcpy(file_name, short_name); + else + strcpy(file_name, row[1]); } - safe_delete_array(query); - return GraveyardID; + + if (safe_x != nullptr) + *safe_x = atof(row[2]); + if (safe_y != nullptr) + *safe_y = atof(row[3]); + if (safe_z != nullptr) + *safe_z = atof(row[4]); + if (graveyard_id != nullptr) + *graveyard_id = atoi(row[5]); + if (maxclients != nullptr) + *maxclients = atoi(row[6]); + + return true; +} + +uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { + + std::string query = StringFormat("SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in GetZoneGraveyardID query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid, float* graveyard_x, float* graveyard_y, float* graveyard_z, float* graveyard_heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if(graveyard_zoneid != 0) - *graveyard_zoneid = atoi(row[0]); - if(graveyard_x != 0) - *graveyard_x = atof(row[1]); - if(graveyard_y != 0) - *graveyard_y = atof(row[2]); - if(graveyard_z != 0) - *graveyard_z = atof(row[3]); - if(graveyard_heading != 0) - *graveyard_heading = atof(row[4]); - mysql_free_result(result); - return true; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetZoneGraveyard query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + if (!results.Success()){ + std::cerr << "Error in GetZoneGraveyard query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return false; + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + + if(graveyard_zoneid != nullptr) + *graveyard_zoneid = atoi(row[0]); + if(graveyard_x != nullptr) + *graveyard_x = atof(row[1]); + if(graveyard_y != nullptr) + *graveyard_y = atof(row[2]); + if(graveyard_z != nullptr) + *graveyard_z = atof(row[3]); + if(graveyard_heading != nullptr) + *graveyard_heading = atof(row[4]); + + return true; } bool Database::LoadZoneNames() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query("SELECT zoneidnumber, short_name FROM zone"); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT zoneidnumber, short_name FROM zone"), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - uint32 zoneid = atoi(row[0]); - std::string zonename = row[1]; - zonename_array.insert(std::pair(zoneid,zonename)); - } - } - else { - std::cerr << "Error in LoadZoneNames query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in LoadZoneNames query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - mysql_free_result(result); + + for (auto row= results.begin();row != results.end();++row) + { + uint32 zoneid = atoi(row[0]); + std::string zonename = row[1]; + zonename_array.insert(std::pair(zoneid,zonename)); + } return true; } uint32 Database::GetZoneID(const char* zonename) { - if (zonename == 0) + + if (zonename == nullptr) return 0; - for (auto iter = zonename_array.begin(); iter != zonename_array.end(); ++iter) { - if (strcasecmp(iter->second.c_str(), zonename) == 0) { + + for (auto iter = zonename_array.begin(); iter != zonename_array.end(); ++iter) + if (strcasecmp(iter->second.c_str(), zonename) == 0) return iter->first; - } - } + return 0; } const char* Database::GetZoneName(uint32 zoneID, bool ErrorUnknown) { auto iter = zonename_array.find(zoneID); - if (iter != zonename_array.end()) { + if (iter != zonename_array.end()) return iter->second.c_str(); - } - else { - if (ErrorUnknown) - return "UNKNOWN"; - else - return 0; - } + + if (ErrorUnknown) + return "UNKNOWN"; + return 0; } uint8 Database::GetPEQZone(uint32 zoneID, uint32 version){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int peqzone = 0; + + std::string query = StringFormat("SELECT peqzone from zone where zoneidnumber='%i' AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT peqzone from zone where zoneidnumber='%i' AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version), errbuf, &result)) + if (!results.Success()) { - if (mysql_num_rows(result) > 0) - { - row = mysql_fetch_row(result); - peqzone = atoi(row[0]); - } - safe_delete_array(query); - mysql_free_result(result); - return peqzone; + std::cerr << "Error in GetPEQZone query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - else - { - std::cerr << "Error in GetPEQZone query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - return peqzone; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } bool Database::CheckNameFilter(const char* name, bool surname) { std::string str_name = name; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; if(surname) { @@ -1440,137 +1172,117 @@ bool Database::CheckNameFilter(const char* name, bool surname) } } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name FROM name_filter"), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - std::string current_row = row[0]; - for(size_t x = 0; x < current_row.size(); ++x) - { - current_row[x] = tolower(current_row[x]); - } + + std::string query("SELECT name FROM name_filter"); + auto results = QueryDatabase(query); - if(str_name.find(current_row) != std::string::npos) - { - return false; - } - } - - mysql_free_result(result); + if (!results.Success()) + { + std::cerr << "Error in CheckNameFilter query '" << query << "' " << results.ErrorMessage() << std::endl; + // false through to true? shouldn't it be falls through to false? return true; } - else + + for (auto row = results.begin();row != results.end();++row) { - std::cerr << "Error in CheckNameFilter query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string current_row = row[0]; + + for(size_t x = 0; x < current_row.size(); ++x) + current_row[x] = tolower(current_row[x]); + + if(str_name.find(current_row) != std::string::npos) + return false; } return true; } bool Database::AddToNameFilter(const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + + std::string query = StringFormat("INSERT INTO name_filter (name) values ('%s')", name); + auto results = QueryDatabase(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO name_filter (name) values ('%s')", name), errbuf, 0, &affected_rows)) { - std::cerr << "Error in AddToNameFilter query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + if (!results.Success()) + { + std::cerr << "Error in AddToNameFilter query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - - if (affected_rows == 0) { + if (results.RowsAffected() == 0) return false; - } return true; } uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID), errbuf, &result)) + std::string query = StringFormat("SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID); + auto results = QueryDatabase(query); + + if (!results.Success()) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 account_id = atoi(row[0]); - if (oAccountName) - strcpy(oAccountName, row[1]); - if (oStatus) - *oStatus = atoi(row[2]); - mysql_free_result(result); - return account_id; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetAccountIDFromLSID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::cerr << "Error in GetAccountIDFromLSID query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + uint32 account_id = atoi(row[0]); + + if (oAccountName) + strcpy(oAccountName, row[1]); + if (oStatus) + *oStatus = atoi(row[2]); + + return account_id; } void Database::GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT name, status FROM account WHERE id=%i", id); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name, status FROM account WHERE id=%i", id), errbuf, &result)) + if (!results.Success()) { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (oAccountName) - strcpy(oAccountName, row[0]); - if (oStatus) - *oStatus = atoi(row[1]); - } - mysql_free_result(result); + std::cerr << "Error in GetAccountFromID query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } - else - std::cerr << "Error in GetAccountFromID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + if (results.RowCount() != 1) + return; + + auto row = results.begin(); + + if (oAccountName) + strcpy(oAccountName, row[0]); + if (oStatus) + *oStatus = atoi(row[1]); } void Database::ClearMerchantTemp(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "delete from merchantlist_temp"), errbuf)) { - std::cerr << "Error in ClearMerchantTemp query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query("delete from merchantlist_temp"); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cerr << "Error in ClearMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; } bool Database::UpdateName(const char* oldname, const char* newname) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - + std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - return false; - } - safe_delete_array(query); - if (affected_rows == 0) - { + std::string query = StringFormat("UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname); + auto results = QueryDatabase(query); + + if (!results.Success()) + return false; + + if (results.RowsAffected() == 0) return false; - } return true; } @@ -1578,74 +1290,53 @@ bool Database::UpdateName(const char* oldname, const char* newname) { // If the name is used or an error occurs, it returns false, otherwise it returns true bool Database::CheckUsedName(const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - //if (strlen(name) > 15) - // return false; - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM character_ where name='%s'", name), errbuf, &result)) { - std::cerr << "Error in CheckUsedName query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT id FROM character_ where name='%s'", name); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in CheckUsedName query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - else { // It was a valid Query, so lets do our counts! - safe_delete_array(query); - uint32 tmp = mysql_num_rows(result); - mysql_free_result(result); - if (tmp > 0) // There is a Name! No change (Return False) - return false; - else // Everything is okay, so we go and do this. - return true; - } + + if (results.RowCount() > 0) + return false; + + return true; } uint8 Database::GetServerType() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT value FROM variables WHERE varname='ServerType'"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint8 ServerType = atoi(row[0]); - mysql_free_result(result); - return ServerType; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetServerType query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - return 0; + std::string query("SELECT value FROM variables WHERE varname='ServerType'"); + auto results = QueryDatabase(query); + if (!results.Success()) + { + std::cerr << "Error in GetServerType query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } bool Database::MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if(zonename == nullptr || strlen(zonename) == 0) - return(false); + return false; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname), errbuf, 0,&affected_rows)) { - std::cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << errbuf << std::endl; + std::string query = StringFormat("UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - if (affected_rows == 0) + if (results.RowsAffected() == 0) return false; return true; @@ -1656,50 +1347,40 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { } bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID), errbuf, 0,&affected_rows)) { - std::cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << errbuf << std::endl; + + std::string query = StringFormat("UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - if (affected_rows == 0) - return false; - - return true; + return results.RowsAffected() != 0; } uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 acctid) { - //TODO: Rewrite better function - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + PlayerProfile_Struct* pp; ExtendedProfile_Struct* ext; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile, extprofile FROM character_ WHERE name='%s'", oldname), errbuf, &result)) { - safe_delete_array(query); + std::string query = StringFormat("SELECT profile, extprofile FROM character_ WHERE name='%s'", oldname); + auto results = QueryDatabase(query); - row = mysql_fetch_row(result); - - pp = (PlayerProfile_Struct*)row[0]; - strcpy(pp->name, newname); - - ext = (ExtendedProfile_Struct*)row[1]; - - mysql_free_result(result); - } - - else { - std::cerr << "Error in CopyCharacter read query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + if (!results.Success()) + { + std::cerr << "Error in CopyCharacter read query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - uint32 affected_rows = 0; + auto row = results.begin(); + + pp = (PlayerProfile_Struct*)row[0]; + strcpy(pp->name, newname); + + ext = (ExtendedProfile_Struct*)row[1]; + char query2[276 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 1]; char* end=query2; @@ -1709,105 +1390,86 @@ uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 a end += DoEscapeString(end, (char*) ext, sizeof(ExtendedProfile_Struct)); end += sprintf(end, "\', account_id=%d, name='%s'", acctid, newname); - if (!RunQuery(query2, (uint32) (end - query2), errbuf, 0, &affected_rows)) { - std::cerr << "Error in CopyCharacter query '" << query << "' " << errbuf << std::endl; + results = QueryDatabase(query2, (uint32) (end - query2)); + + if (!results.Success()) + { + std::cerr << "Error in CopyCharacter query '" << query2 << "' " << results.ErrorMessage() << std::endl; return 0; } - if (affected_rows == 0) { + if (results.RowsAffected() == 0) return 0; - } return 1; } bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked), errbuf, 0,&affected_rows)) { - std::cerr << "Error in SetHackerFlag query '" << query << "' " << errbuf << std::endl; - return false; - } - safe_delete_array(query); + + std::string query = StringFormat("INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked); + auto results = QueryDatabase(query); - if (affected_rows == 0) + if (!results.Success()) { + std::cerr << "Error in SetHackerFlag query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return true; + return results.RowsAffected() != 0; } -bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. +bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { + + //Utilize the "hacker" table, but also give zone information. + std::string query = StringFormat("INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone); + auto results = QueryDatabase(query); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone), errbuf, 0,&affected_rows)) { - std::cerr << "Error in SetMQDetectionFlag query '" << query << "' " << errbuf << std::endl; - return false; - } - - safe_delete_array(query); - - if (affected_rows == 0) + if (!results.Success()) { + std::cerr << "Error in SetMQDetectionFlag query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return true; + return results.RowsAffected() != 0; } uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race) { uint16 race_cap = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - MYSQL_RES *result; - MYSQL_ROW row; - + //Check for a racial cap! - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skillcap from race_skillcaps where skill = %i && race = %i", skillid, in_race), errbuf, &result, &affected_rows)) - { - if (affected_rows != 0) - { - row = mysql_fetch_row(result); - race_cap = atoi(row[0]); - } - delete[] query; - mysql_free_result(result); - } + std::string query = StringFormat("SELECT skillcap from race_skillcaps where skill = %i && race = %i", skillid, in_race); + auto results = QueryDatabase(query); - return race_cap; + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level) { uint8 skill_level = 0, skill_formula = 0; uint16 base_cap = 0, skill_cap = 0, skill_cap2 = 0, skill_cap3 = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - MYSQL_RES *result; - MYSQL_ROW row; + + //Fetch the data from DB. - if (RunQuery(query, MakeAnyLenString(&query, "SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class), errbuf, &result, &affected_rows)) + std::string query = StringFormat("SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class); + auto results = QueryDatabase(query); + + if (results.Success() && results.RowsAffected() != 0) { - if (affected_rows != 0) - { - row = mysql_fetch_row(result); - skill_level = atoi(row[0]); - skill_formula = atoi(row[1]); - skill_cap = atoi(row[2]); - if (atoi(row[3]) > skill_cap) - skill_cap2 = (atoi(row[3])-skill_cap)/10; //Split the post-50 skill cap into difference between pre-50 cap and post-50 cap / 10 to determine amount of points per level. - skill_cap3 = atoi(row[4]); - } - delete[] query; - mysql_free_result(result); + auto row = results.begin(); + skill_level = atoi(row[0]); + skill_formula = atoi(row[1]); + skill_cap = atoi(row[2]); + if (atoi(row[3]) > skill_cap) + skill_cap2 = (atoi(row[3])-skill_cap)/10; //Split the post-50 skill cap into difference between pre-50 cap and post-50 cap / 10 to determine amount of points per level. + skill_cap3 = atoi(row[4]); } int race_skill = GetRaceSkill(skillid,in_race); @@ -1826,685 +1488,619 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 base_cap = in_level*skill_formula+skill_formula; if (base_cap > skill_cap || skill_formula == 0) base_cap = skill_cap; + //If post 50, add post 50 cap to base cap. if (in_level > 50 && skill_cap2 > 0) base_cap += skill_cap2*(in_level-50); + //No cap should ever go above its post50cap if (skill_cap3 > 0 && base_cap > skill_cap3) base_cap = skill_cap3; + //Base cap is now the max value at the person's level, return it! return base_cap; } uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 charid = atoi(row[0]); - if (oAccID) - *oAccID = atoi(row[1]); - if (oZoneID) - *oZoneID = GetZoneID(row[2]); - if(oInstanceID) - *oInstanceID = atoi(row[3]); - if (oX) - *oX = atof(row[4]); - if (oY) - *oY = atof(row[5]); - if (oZ) - *oZ = atof(row[6]); - mysql_free_result(result); - return charid; - } - mysql_free_result(result); + if (!results.Success()) + { + std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - else { - std::cerr << "Error in GetCharacterInfo query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return 0; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + uint32 charid = atoi(row[0]); + if (oAccID) + *oAccID = atoi(row[1]); + if (oZoneID) + *oZoneID = GetZoneID(row[2]); + if(oInstanceID) + *oInstanceID = atoi(row[3]); + if (oX) + *oX = atof(row[4]); + if (oY) + *oY = atof(row[5]); + if (oZ) + *oZ = atof(row[6]); + + return charid; } bool Database::UpdateLiveChar(char* charname,uint32 lsaccount_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET charname='%s' WHERE id=%i;",charname, lsaccount_id), errbuf)) { - std::cerr << "Error in UpdateLiveChar query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("UPDATE account SET charname='%s' WHERE id=%i;",charname, lsaccount_id); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in UpdateLiveChar query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; } bool Database::GetLiveChar(uint32 account_id, char* cname) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT charname FROM account WHERE id=%i", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - strcpy(cname,row[0]); - mysql_free_result(result); - return true; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetLiveChar query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("SELECT charname FROM account WHERE id=%i", account_id); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in GetLiveChar query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; } - return false; + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + strcpy(cname,row[0]); + + return true; } void Database::SetLFP(uint32 CharID, bool LFP) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfp=%i where id=%i",LFP, CharID), ErrBuf)) - LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); - - safe_delete_array(Query); + std::string query = StringFormat("update character_ set lfp=%i where id=%i",LFP, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { + + std::string query = StringFormat("update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID); + auto results = QueryDatabase(query); - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID), ErrBuf)) - LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); - - safe_delete_array(Query); - + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } void Database::SetLFG(uint32 CharID, bool LFG) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfg=%i where id=%i",LFG, CharID), ErrBuf)) - LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); - - safe_delete_array(Query); + std::string query = StringFormat("update character_ set lfg=%i where id=%i",LFG, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { + + std::string query = StringFormat( "update character_ set firstlogon=%i where id=%i",firstlogon, CharID); + auto results = QueryDatabase(query); - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set firstlogon=%i where id=%i",firstlogon, CharID), ErrBuf)) - LogFile->write(EQEMuLog::Error, "Error updating firstlogon for character %i : %s", CharID, ErrBuf); - - safe_delete_array(Query); - + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error updating firstlogon for character %i : %s", CharID, results.ErrorMessage().c_str()); } void Database::AddReport(std::string who, std::string against, std::string lines) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char *Query = 0; + char *escape_str = new char[lines.size()*2+1]; DoEscapeString(escape_str, lines.c_str(), lines.size()); - if (!RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", who.c_str(), against.c_str(), escape_str), ErrBuf)) - LogFile->write(EQEMuLog::Error, "Error adding a report for %s: %s", who.c_str(), ErrBuf); - - safe_delete_array(Query); + std::string query = StringFormat("INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", who.c_str(), against.c_str(), escape_str); + auto results = QueryDatabase(query); safe_delete_array(escape_str); + + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error adding a report for %s: %s", who.c_str(), results.ErrorMessage().c_str()); } -void Database::SetGroupID(const char* name,uint32 id, uint32 charid, uint32 ismerc){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(id == 0){ //removing you from table - if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc), errbuf)) - LogFile->write(EQEMuLog::Error, "Error deleting character from group id: %s", errbuf); +void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc){ + + std::string query; + if (id == 0) + { + // removing from group + query = StringFormat("delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc); + auto results = QueryDatabase(query); + + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error deleting character from group id: %s", results.ErrorMessage().c_str()); + + return; } - else{ - if (!RunQuery(query, MakeAnyLenString(&query, "replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc), errbuf)) - LogFile->write(EQEMuLog::Error, "Error adding character to group id: %s", errbuf); - } - safe_delete_array(query); + + // adding to group + query = StringFormat("replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc); + auto results = QueryDatabase(query); + + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error adding character to group id: %s", results.ErrorMessage().c_str()); +} + +void Database::ClearAllGroups(void) +{ + std::string query("delete from group_id"); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear groups: " << results.ErrorMessage() << std::endl; + + return; } void Database::ClearGroup(uint32 gid) { ClearGroupLeader(gid); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(gid == 0) { //clear all groups - //if (!RunQuery(query, MakeAnyLenString(&query, "update group_id set groupid=0 where groupid!=0"), errbuf)) - if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id"), errbuf)) - printf("Unable to clear groups: %s\n",errbuf); - } else { //clear a specific group - //if (!RunQuery(query, MakeAnyLenString(&query, "update group_id set groupid=0 where groupid = %lu", gid), errbuf)) - if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id where groupid = %lu", (unsigned long)gid), errbuf)) - printf("Unable to clear groups: %s\n",errbuf); + + if(gid == 0) + { + //clear all groups + ClearAllGroups(); + return; } - safe_delete_array(query); + + //clear a specific group + std::string query = StringFormat("delete from group_id where groupid = %lu", (unsigned long)gid); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear groups: " << results.ErrorMessage() << std::endl; } uint32 Database::GetGroupID(const char* name){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 groupid=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT groupid from group_id where name='%s'", name), errbuf, &result)) { - if((row = mysql_fetch_row(result))) - { - if(row[0]) - groupid=atoi(row[0]); - } - else - LogFile->write(EQEMuLog::Debug, "Character not in a group: %s", name); - mysql_free_result(result); + + std::string query = StringFormat("SELECT groupid from group_id where name='%s'", name); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error getting group id: %s", results.ErrorMessage().c_str()); + return 0; } - else - LogFile->write(EQEMuLog::Error, "Error getting group id: %s", errbuf); - safe_delete_array(query); - return groupid; + + if (results.RowCount() == 0) + { + LogFile->write(EQEMuLog::Debug, "Character not in a group: %s", name); + return 0; + } + + auto row = results.begin(); + + return atoi(row[0]); } char* Database::GetGroupLeaderForLogin(const char* name,char* leaderbuf){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + PlayerProfile_Struct pp; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile from character_ where name='%s'", name), errbuf, &result)) { - row = mysql_fetch_row(result); - unsigned long* lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - strcpy(leaderbuf,pp.groupMembers[0]); - } - mysql_free_result(result); + + std::string query = StringFormat("SELECT profile from character_ where name='%s'", name); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cout << "Unable to get leader name: " << results.ErrorMessage() << std::endl; + return leaderbuf; } - else{ - printf("Unable to get leader name: %s\n",errbuf); - } - safe_delete_array(query); + + if (results.LengthOfColumn(0) != sizeof(PlayerProfile_Struct)) + return leaderbuf; + + auto row = results.begin(); + + memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); + strcpy(leaderbuf,pp.groupMembers[0]); + return leaderbuf; } void Database::SetGroupLeaderName(uint32 gid, const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name), errbuf)) - printf("Unable to set group leader: %s\n",errbuf); + std::string query = StringFormat("Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name); + auto results = QueryDatabase(query); - safe_delete_array(query); + if (!results.Success()) + std::cout << "Unable to set group leader: " << results.ErrorMessage() << std::endl; } char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; + + std::string query = StringFormat("SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid), - errbuf, &result)) { + if (!results.Success() || results.RowCount() == 0) + { + if(leaderbuf) + strcpy(leaderbuf, "UNKNOWN"); - safe_delete_array(query); + if(maintank) + maintank[0] = '\0'; - row = mysql_fetch_row(result); - unsigned long* Lengths = mysql_fetch_lengths(result); + if(assist) + assist[0] = '\0'; - if(row != nullptr){ + if(puller) + puller[0] = '\0'; - if(leaderbuf) - strcpy(leaderbuf, row[0]); + if(marknpc) + marknpc[0] = '\0'; - if(maintank) - strcpy(maintank, row[1]); - - if(assist) - strcpy(assist, row[2]); - - if(puller) - strcpy(puller, row[3]); - - if(marknpc) - strcpy(marknpc, row[4]); - - if(GLAA && (Lengths[5] == sizeof(GroupLeadershipAA_Struct))) - memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); - - mysql_free_result(result); - return leaderbuf; - } + return leaderbuf; } - else - safe_delete_array(query); + + auto row = results.begin(); if(leaderbuf) - strcpy(leaderbuf, "UNKNOWN"); + strcpy(leaderbuf, row[0]); if(maintank) - maintank[0] = 0; + strcpy(maintank, row[1]); if(assist) - assist[0] = 0; + strcpy(assist, row[2]); if(puller) - puller[0] = 0; + strcpy(puller, row[3]); if(marknpc) - marknpc[0] = 0; + strcpy(marknpc, row[4]); + + if(GLAA && results.LengthOfColumn(5) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); return leaderbuf; } -void Database::ClearGroupLeader(uint32 gid){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(gid == 0) { //clear all group leaders - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from group_leaders"), errbuf)) - printf("Unable to clear group leaders: %s\n",errbuf); - } else { //clear a specific group leader - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from group_leaders where gid = %lu", (unsigned long)gid), errbuf)) - printf("Unable to clear group leader: %s\n",errbuf); - } - safe_delete_array(query); +// Clearing all group leaders +void Database::ClearAllGroupLeaders(void) +{ + std::string query("DELETE from group_leaders"); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear group leaders: " << results.ErrorMessage() << std::endl; + + return; } -bool FetchRowMap(MYSQL_RES *result, std::map &rowmap) -{ -MYSQL_FIELD *fields; -MYSQL_ROW row; -unsigned long num_fields,i; -bool retval=false; - rowmap.clear(); - if (result && (num_fields=mysql_num_fields(result)) && (row = mysql_fetch_row(result))!=nullptr && (fields = mysql_fetch_fields(result))!=nullptr) { - retval=true; - for(i=0;i %u", count,count), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) { - row = mysql_fetch_row(result); - if(atoi(row[0]) <= max) { - count = atoi(row[0]); - mysql_free_result(result); - } else { - mysql_free_result(result); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id > %u ORDER BY id", count), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) { - count++; - while((row = mysql_fetch_row(result))) { - if(count < atoi(row[0])) { - instance_id = count; - mysql_free_result(result); - return true; - } else if(count > max) { - instance_id = 0; - mysql_free_result(result); - return false; - } else { - count++; - } - } - } else { - instance_id = 0; - mysql_free_result(result); - return false; - } - } else { - safe_delete_array(query); - instance_id = 0; - return false; - } - } - } else { - instance_id = 0; - mysql_free_result(result); - return false; - } - } else { - safe_delete_array(query); + + std::string query = StringFormat("SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count, count); + auto results = QueryDatabase(query); + + if (!results.Success()) + { instance_id = 0; return false; } + + if (results.RowCount() == 0) + { + instance_id = 0; + return false; + } + + auto row = results.begin(); + + if (atoi(row[0]) <= max) + { + instance_id = atoi(row[0]); + return true; + } + + query = StringFormat("SELECT id FROM instance_list where id > %u ORDER BY id", count); + results = QueryDatabase(query); + + if (!results.Success()) + { + instance_id = 0; + return false; + } + + if (results.RowCount() == 0) + { + instance_id = 0; + return false; + } + + count++; + for (auto row = results.begin();row != results.end();++row) + { + if(count < atoi(row[0])) + { + instance_id = count; + return true; + } + + if(count > max) + { + instance_id = 0; + return false; + } + + count++; + } + instance_id = count; return true; } @@ -2574,214 +2175,114 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id) //perhaps purge any expireds too bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO instance_list (id, zone, version, start_time, duration)" - " values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", (unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration), errbuf)) - { - safe_delete_array(query); - return true; - } - else - { - safe_delete_array(query); - return false; - } + std::string query = StringFormat("INSERT INTO instance_list (id, zone, version, start_time, duration)" + " values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", + (unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration); + auto results = QueryDatabase(query); + + return results.Success(); } void Database::PurgeExpiredInstances() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint16 id = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where " - "(start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0"), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) > 0) - { - row = mysql_fetch_row(result); - while(row != nullptr) - { - id = atoi(row[0]); - DeleteInstance(id); - row = mysql_fetch_row(result); - } - } - mysql_free_result(result); - } - else - { - safe_delete_array(query); - } + std::string query("SELECT id FROM instance_list where (start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0"); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + if (results.RowCount() == 0) + return; + + for (auto row = results.begin();row != results.end();++row) + DeleteInstance(atoi(row[0])); } bool Database::AddClientToInstance(uint16 instance_id, uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT INTO instance_list_player(id, charid) values(%lu, %lu)", + (unsigned long)instance_id, (unsigned long)char_id); + auto results = QueryDatabase(query); - if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO instance_list_player(id, charid) " - "values(%lu, %lu)", (unsigned long)instance_id, (unsigned long)char_id), errbuf)) - { - safe_delete_array(query); - return true; - } - else - { - safe_delete_array(query); - return false; - } + return results.Success(); } bool Database::RemoveClientFromInstance(uint16 instance_id, uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + + std::string query = StringFormat("DELETE FROM instance_list_player WHERE id=%lu AND charid=%lu", + (unsigned long)instance_id, (unsigned long)char_id); + auto results = QueryDatabase(query); - if(RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE id=%lu AND charid=%lu", - (unsigned long)instance_id, (unsigned long)char_id), errbuf)) - { - safe_delete_array(query); - return true; - } - else - { - safe_delete_array(query); - return false; - } + return results.Success(); } bool Database::RemoveClientsFromInstance(uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("DELETE FROM instance_list_player WHERE id=%lu", (unsigned long)instance_id); + auto results = QueryDatabase(query); - if(RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_list_player WHERE id=%lu", - (unsigned long)instance_id), errbuf)) - { - safe_delete_array(query); - return true; - } - else - { - safe_delete_array(query); - return false; - } + return results.Success(); } bool Database::CheckInstanceExists(uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM instance_list where id=%u", instance_id), - errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) - { - mysql_free_result(result); - return true; - } - mysql_free_result(result); + std::string query = StringFormat("SELECT * FROM instance_list where id=%u", instance_id); + auto results = QueryDatabase(query); + + if (!results.Success()) return false; - } - else - { - safe_delete_array(query); + + if (results.RowCount() == 0) return false; - } - return false; + + return true; } void Database::BuryCorpsesInInstance(uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if(RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried=1, instanceid=0 WHERE instanceid=%u", - instance_id), errbuf, &result)) - { - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("UPDATE player_corpses SET IsBurried=1, instanceid=0 WHERE instanceid=%u", instance_id); + auto results = QueryDatabase(query); } uint16 Database::GetInstanceVersion(uint16 instance_id) { - if(instance_id < 1) + if(instance_id == 0) return 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 ret; + std::string query = StringFormat("SELECT version FROM instance_list where id=%u", instance_id); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT version FROM instance_list where id=%u", instance_id), - errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) - { - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - else - { - mysql_free_result(result); - return 0; - } - } - else - { - safe_delete_array(query); + if (!results.Success()) return 0; - } - return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } uint16 Database::GetInstanceID(const char* zone, uint32 charid, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint16 ret; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT instance_list.id FROM instance_list, instance_list_player " + std::string query = StringFormat("SELECT instance_list.id FROM instance_list, instance_list_player " "WHERE instance_list.zone=%u AND instance_list.version=%u AND instance_list.id=instance_list_player.id AND " - "instance_list_player.charid=%u LIMIT 1;", GetZoneID(zone), version, charid, charid), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) - { - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - else - { - mysql_free_result(result); - return 0; - } - } - else - { - safe_delete_array(query); + "instance_list_player.charid=%u LIMIT 1;", GetZoneID(zone), version, charid, charid); + auto results = QueryDatabase(query); + + if (!results.Success()) return 0; - } - return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } uint16 Database::GetInstanceID(uint32 zone, uint32 charid, int16 version) @@ -2789,113 +2290,74 @@ uint16 Database::GetInstanceID(uint32 zone, uint32 charid, int16 version) if(!zone) return 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint16 ret; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT instance_list.id FROM instance_list, instance_list_player " + std::string query = StringFormat("SELECT instance_list.id FROM instance_list, instance_list_player " "WHERE instance_list.zone=%u AND instance_list.version=%u AND instance_list.id=instance_list_player.id AND " - "instance_list_player.charid=%u LIMIT 1;", zone, version, charid), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) != 0) - { - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - else - { - mysql_free_result(result); - return 0; - } - } - else - { - safe_delete_array(query); + "instance_list_player.charid=%u LIMIT 1;", zone, version, charid); + auto results = QueryDatabase(query); + + if (!results.Success()) return 0; - } - return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } void Database::GetCharactersInInstance(uint16 instance_id, std::list &charid_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT charid FROM instance_list_player WHERE id=%u", instance_id), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - charid_list.push_back(atoi(row[0])); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetCharactersInInstace query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT charid FROM instance_list_player WHERE id=%u", instance_id); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error in GetCharactersInInstace query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; } + + for(auto row=results.begin();row != results.end();++row) + charid_list.push_back(atoi(row[0])); } void Database::AssignGroupToInstance(uint32 gid, uint32 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + uint32 zone_id = ZoneIDFromInstanceID(instance_id); uint16 version = VersionFromInstanceID(instance_id); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT charid FROM group_id WHERE groupid=%u", gid), - errbuf, &result)) + std::string query = StringFormat("SELECT charid FROM group_id WHERE groupid=%u", gid); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row=results.begin();row != results.end();++row) { - safe_delete_array(query); - while((row = mysql_fetch_row(result)) != nullptr) - { - uint32 charid = atoi(row[0]); - if(GetInstanceID(zone_id, charid, version) == 0) - { - AddClientToInstance(instance_id, charid); - } - } - mysql_free_result(result); - } - else - { - safe_delete_array(query); + uint32 charid = atoi(row[0]); + if(GetInstanceID(zone_id, charid, version) == 0) + AddClientToInstance(instance_id, charid); } } void Database::AssignRaidToInstance(uint32 rid, uint32 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + uint32 zone_id = ZoneIDFromInstanceID(instance_id); uint16 version = VersionFromInstanceID(instance_id); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT charid FROM raid_members WHERE raidid=%u", rid), - errbuf, &result)) + std::string query = StringFormat("SELECT charid FROM raid_members WHERE raidid=%u", rid); + auto results = QueryDatabase(query); + + if (!results.Success()) + return; + + for (auto row=results.begin();row!=results.end();++row) { - safe_delete_array(query); - while((row = mysql_fetch_row(result)) != nullptr) - { - uint32 charid = atoi(row[0]); - if(GetInstanceID(zone_id, charid, version) == 0) - { - AddClientToInstance(instance_id, charid); - } - } - mysql_free_result(result); - } - else - { - safe_delete_array(query); + uint32 charid = atoi(row[0]); + if(GetInstanceID(zone_id, charid, version) == 0) + AddClientToInstance(instance_id, charid); } } @@ -2934,208 +2396,127 @@ void Database::FlagInstanceByRaidLeader(uint32 zone, int16 version, uint32 chari void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(RunQuery(query, MakeAnyLenString(&query, "UPDATE `instance_list` SET start_time=UNIX_TIMESTAMP(), " - "duration=%u WHERE id=%u", new_duration, instance_id), errbuf)) - { - safe_delete_array(query); - } - else - { - //error - safe_delete_array(query); - } + std::string query = StringFormat("UPDATE `instance_list` SET start_time=UNIX_TIMESTAMP(), " + "duration=%u WHERE id=%u", new_duration, instance_id); + auto results = QueryDatabase(query); } bool Database::GlobalInstance(uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool ret; + + std::string query = StringFormat("SELECT is_global from instance_list where id=%u LIMIT 1", instance_id); + auto results = QueryDatabase(query); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT is_global from instance_list where id=%u LIMIT 1", instance_id), errbuf, &result)) - { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row) - { - ret = (atoi(row[0]) == 1) ? true : false; - } - else - { - mysql_free_result(result); - return false; - } - } - else - { - safe_delete_array(query); + if (!results.Success()) return false; - } - return ret; + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + return (atoi(row[0]) == 1) ? true : false; } void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected = 0; - + std::string field; - if(win) + switch(theme) { - switch(theme) + case 1: { - case 1: - { - field = "guk_wins"; - break; - } - case 2: - { - field = "mir_wins"; - break; - } - case 3: - { - field = "mmc_wins"; - break; - } - case 4: - { - field = "ruj_wins"; - break; - } - case 5: - { - field = "tak_wins"; - break; - } - default: - { - return; - } + field = "guk_"; + break; } - } - else - { - switch(theme) + case 2: { - case 1: - { - field = "guk_losses"; - break; - } - case 2: - { - field = "mir_losses"; - break; - } - case 3: - { - field = "mmc_losses"; - break; - } - case 4: - { - field = "ruj_losses"; - break; - } - case 5: - { - field = "tak_losses"; - break; - } - default: - { - return; - } + field = "mir_"; + break; + } + case 3: + { + field = "mmc_"; + break; + } + case 4: + { + field = "ruj_"; + break; + } + case 5: + { + field = "tak_"; + break; + } + default: + { + return; } } - if(RunQuery(query, MakeAnyLenString(&query, "UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u", - field.c_str(), field.c_str(), char_id), errbuf, nullptr, &affected)) - { - safe_delete_array(query); - } + if (win) + field += "wins"; else - { - //error - safe_delete_array(query); - } + field += "losses"; - if(affected == 0) - { - if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `adventure_stats` SET %s=1, player_id=%u", - field.c_str(), char_id), errbuf)) - { - safe_delete_array(query); - } - else - { - //error - safe_delete_array(query); - } - } + std::string query = StringFormat("UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u",field.c_str(), field.c_str(), char_id); + auto results = QueryDatabase(query); + + if (results.RowsAffected() != 0) + return; + + query = StringFormat("INSERT INTO `adventure_stats` SET %s=1, player_id=%u", field.c_str(), char_id); + QueryDatabase(query); } bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, uint32 &tak_w, uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, " - "`guk_losses`, `mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", - char_id), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result)) != nullptr) - { - guk_w = atoi(row[0]); - mir_w = atoi(row[1]); - mmc_w = atoi(row[2]); - ruj_w = atoi(row[3]); - tak_w = atoi(row[4]); - guk_l = atoi(row[5]); - mir_l = atoi(row[6]); - mmc_l = atoi(row[7]); - ruj_l = atoi(row[8]); - tak_l = atoi(row[9]); - } - mysql_free_result(result); - return true; - } - else - { - safe_delete_array(query); + std::string query = StringFormat("SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, `guk_losses`, " + "`mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", char_id); + auto results = QueryDatabase(query); + + if (!results.Success()) return false; - } + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + guk_w = atoi(row[0]); + mir_w = atoi(row[1]); + mmc_w = atoi(row[2]); + ruj_w = atoi(row[3]); + tak_w = atoi(row[4]); + guk_l = atoi(row[5]); + mir_l = atoi(row[6]); + mmc_l = atoi(row[7]); + ruj_l = atoi(row[8]); + tak_l = atoi(row[9]); + + return true; } -uint32 Database::GetGuildDBIDByCharID(uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - int retVal = 0; +uint32 Database::GetGuildIDByCharID(uint32 char_id) +{ - if (RunQuery(query, MakeAnyLenString(&query, "SELECT guild_id FROM guild_members WHERE char_id='%i'", char_id), errbuf, &result)) { - if (mysql_num_rows(result) == 1) { - MYSQL_ROW row = mysql_fetch_row(result); - retVal = atoi(row[0]); - } - mysql_free_result(result); + std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id='%i'", char_id); + auto results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in GetGuildIDByChar query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - else { - std::cerr << "Error in GetAccountIDByChar query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - return retVal; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } diff --git a/common/database.h b/common/database.h index 300fc9875..9be05f0cd 100644 --- a/common/database.h +++ b/common/database.h @@ -130,7 +130,7 @@ public: uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); uint32 GetAccountIDByChar(uint32 char_id); uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0); - uint32 GetGuildDBIDByCharID(uint32 char_id); + uint32 GetGuildIDByCharID(uint32 char_id); void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0); void GetCharName(uint32 char_id, char* name); uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0,float* oX = 0, float* oY = 0, float* oZ = 0); @@ -207,6 +207,7 @@ public: char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); void ClearGroupLeader(uint32 gid = 0); + /* * Raids @@ -223,7 +224,7 @@ public: bool SetVariable(const char* varname, const char* varvalue); bool LoadVariables(); uint32 LoadVariables_MQ(char** query); - bool LoadVariables_result(MYSQL_RES* result); + bool LoadVariables_result(MySQLRequestResult results); /* * General Queries @@ -262,7 +263,20 @@ private: uint32 varcache_max; VarCache_Struct** varcache_array; uint32 varcache_lastupdate; + + + /* + * Groups, utility methods. + */ + void ClearAllGroupLeaders(); + void ClearAllGroups(); + + + /* + * Raid, utility methods. + */ + void ClearAllRaids(); + void ClearAllRaidDetails(); }; -bool FetchRowMap(MYSQL_RES *result, std::map &rowmap); #endif diff --git a/common/dbcore.cpp b/common/dbcore.cpp index 9b6236e5a..3d25236c7 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -80,10 +80,10 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo pStatus = Error; // error appears to be a disconnect error, may need to try again. - if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) + if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) { - if (retryOnFailureOnce) + if (retryOnFailureOnce) { std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl; MySQLRequestResult requestResult = QueryDatabase(query, querylen, false); @@ -95,18 +95,18 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo } } - + pStatus = Error; - + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - + std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32)mysql_errno(&mysql), errorBuffer); - } - + } + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); @@ -114,21 +114,26 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; #endif return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer); - + } // successful query. get results. MYSQL_RES* res = mysql_store_result(&mysql); - MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), (uint32)mysql_num_rows(res), (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); + uint32 rowCount = 0; + + if (res != nullptr) + rowCount = (uint32)mysql_num_rows(res); + + MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), rowCount, (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); #if DEBUG_MYSQL_QUERIES >= 1 - if (requestResult.Success()) + if (requestResult.Success()) { std::cout << "query successful"; if (requestResult.Result()) std::cout << ", " << (int) mysql_num_rows(requestResult.Result()) << " rows returned"; - + std::cout << ", " << requestResult.RowCount() << " rows affected"; std::cout<< std::endl; } diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index a1fa5e3e1..4c772689d 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -237,7 +237,7 @@ std::string EmuConstants::InventorySubName(int16 sub) { return "Unknown Sub"; std::string ret_str; - StringFormat(ret_str, "Container %i", (sub + 1)); // zero-based index..but, count starts at one + ret_str = StringFormat("Container %i", (sub + 1)); // zero-based index..but, count starts at one return ret_str; } @@ -250,7 +250,7 @@ std::string EmuConstants::InventoryAugName(int16 aug) { return "Unknown Aug"; std::string ret_str; - StringFormat(ret_str, "Augment %i", (aug + 1)); // zero-based index..but, count starts at one + ret_str = StringFormat("Augment %i", (aug + 1)); // zero-based index..but, count starts at one return ret_str; } @@ -929,7 +929,7 @@ uint64 EQLimits::PossessionsBitmask(uint32 version) { /*Underfoot*/ 0x000000027FFFFFFF, /*RoF*/ 0x00000003FFFFFFFF, /*RoF2*/ 0, - + /*NPC*/ 0, /*Merc*/ 0, /*Bot*/ 0, @@ -950,7 +950,7 @@ uint64 EQLimits::EquipmentBitmask(uint32 version) { /*Underfoot*/ 0x00000000007FFFFF, /*RoF*/ 0x00000000007FFFFF, /*RoF2*/ 0, - + /*NPC*/ 0, /*Merc*/ 0, /*Bot*/ 0, @@ -971,7 +971,7 @@ uint64 EQLimits::GeneralBitmask(uint32 version) { /*Underfoot*/ 0x000000007F800000, /*RoF*/ 0x00000001FF800000, /*RoF2*/ 0, - + /*NPC*/ 0, /*Merc*/ 0, /*Bot*/ 0, @@ -992,7 +992,7 @@ uint64 EQLimits::CursorBitmask(uint32 version) { /*Underfoot*/ 0x0000000200000000, /*RoF*/ 0x0000000200000000, /*RoF2*/ 0, - + /*NPC*/ 0, /*Merc*/ 0, /*Bot*/ 0, @@ -1013,13 +1013,13 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) { /*Underfoot*/ Underfoot::limits::ALLOWS_EMPTY_BAG_IN_BAG, /*RoF*/ RoF::limits::ALLOWS_EMPTY_BAG_IN_BAG, /*RoF2*/ false, - + /*NPC*/ false, /*Merc*/ false, /*Bot*/ false, /*Pet*/ false }; - + return false; // not implemented //return local[ValidateMobVersion(version)]; } @@ -1035,7 +1035,7 @@ uint16 EQLimits::ItemCommonSize(uint32 version) { /*Underfoot*/ EmuConstants::ITEM_COMMON_SIZE, /*RoF*/ EmuConstants::ITEM_COMMON_SIZE, /*RoF2*/ 0, - + /*NPC*/ EmuConstants::ITEM_COMMON_SIZE, /*Merc*/ EmuConstants::ITEM_COMMON_SIZE, /*Bot*/ EmuConstants::ITEM_COMMON_SIZE, @@ -1055,7 +1055,7 @@ uint16 EQLimits::ItemContainerSize(uint32 version) { /*Underfoot*/ EmuConstants::ITEM_CONTAINER_SIZE, /*RoF*/ EmuConstants::ITEM_CONTAINER_SIZE, /*RoF2*/ 0, - + /*NPC*/ EmuConstants::ITEM_CONTAINER_SIZE, /*Merc*/ EmuConstants::ITEM_CONTAINER_SIZE, /*Bot*/ EmuConstants::ITEM_CONTAINER_SIZE, @@ -1075,7 +1075,7 @@ bool EQLimits::CoinHasWeight(uint32 version) { /*Underfoot*/ Underfoot::limits::COIN_HAS_WEIGHT, /*RoF*/ RoF::limits::COIN_HAS_WEIGHT, /*RoF2*/ true, - + /*NPC*/ true, /*Merc*/ true, /*Bot*/ true, @@ -1095,7 +1095,7 @@ uint32 EQLimits::BandoliersCount(uint32 version) { /*Underfoot*/ EmuConstants::BANDOLIERS_COUNT, /*RoF*/ EmuConstants::BANDOLIERS_COUNT, /*RoF2*/ 0, - + /*NPC*/ NOT_USED, /*Merc*/ NOT_USED, /*Bot*/ NOT_USED, @@ -1115,7 +1115,7 @@ uint32 EQLimits::BandolierSize(uint32 version) { /*Underfoot*/ EmuConstants::BANDOLIER_SIZE, /*RoF*/ EmuConstants::BANDOLIER_SIZE, /*RoF2*/ 0, - + /*NPC*/ NOT_USED, /*Merc*/ NOT_USED, /*Bot*/ NOT_USED, @@ -1135,7 +1135,7 @@ uint32 EQLimits::PotionBeltSize(uint32 version) { /*Underfoot*/ EmuConstants::POTION_BELT_SIZE, /*RoF*/ EmuConstants::POTION_BELT_SIZE, /*RoF2*/ 0, - + /*NPC*/ NOT_USED, /*Merc*/ NOT_USED, /*Bot*/ NOT_USED, diff --git a/common/logsys_eqemu.cpp b/common/logsys_eqemu.cpp index 5086114f5..e1e75996c 100644 --- a/common/logsys_eqemu.cpp +++ b/common/logsys_eqemu.cpp @@ -33,9 +33,7 @@ void log_message(LogType type, const char *fmt, ...) { } void log_messageVA(LogType type, const char *fmt, va_list args) { - std::string prefix_buffer; - - StringFormat(prefix_buffer, "[%s] ", log_type_info[type].name); + std::string prefix_buffer = StringFormat("[%s] ", log_type_info[type].name); LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args); } diff --git a/common/ruletypes.h b/common/ruletypes.h index 0fd413e5a..91ade257b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -76,6 +76,7 @@ RULE_BOOL ( Character, SharedBankPlat, false) //off by default to prevent duping RULE_BOOL ( Character, BindAnywhere, false) RULE_INT ( Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen. RULE_INT ( Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. +RULE_INT ( Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target. RULE_BOOL ( Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not. RULE_INT ( Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA RULE_INT ( Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index 2a1921d5d..6cad4ade0 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -1,12 +1,15 @@ #! /usr/bin/env python # -# This script generates cross-references to show associated (handled) opcodes -# between the server and client. It will generate files for each client and -# server found, and provide some basic information..such as opcode names and -# values, server handler and whether opcodes are translated on tx/rx, etc... -# -# It's currently limited to the 'Zone' server..but, can be expounded upon to -# include other servers and clients, and other criteria and features. + +""" +'Opcode-Handlers' for EQEmulator + +This script generates cross-references to show associated (handled) opcodes +between the server and client. It will generate files for each client and +server found, and provide some basic information..such as opcode names and +values, server handler and whether opcodes are translated on tx/rx, etc... + +""" import sys @@ -15,122 +18,133 @@ import os from time import time, ctime -DEBUG = 1 # {0 - normal, 1 - verbose, 2 - in-depth} +VERBOSE = False # messaging: {False - minimal, True - robust} base_path = os.getcwd()[:-14] # '/utils/scripts' +base_path = base_path.replace('\\', '/') client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF', 'RoF2', 'ClientTest'] server_list = ['Login', 'World', 'Zone', 'UCS', 'ServerTest'] -client_opcodes = {} -server_opcodes = {} +client_opcodes = {} # x[key='Client'][key='OP_CodeName'](value='0x####') +server_opcodes = {} # x[key='OP_CodeName'](value=) - opcodes apply to all servers -client_encodes = {} -client_decodes = {} +client_encodes = {} # x[key='Client'](value='OP_CodeName') +client_decodes = {} # x[key='Client'](value='OP_CodeName') -server_handlers = {} +server_handlers = {} # x[key='Server'][key='OP_CodeName'](value='[%X] Near Handler::ReferenceFunction') -out_files = {} +out_files = {} # x[key='Server'](value=) + +#statistics = {} +#report_entries = {} def main(): - """ Call each method independently and track success """ + """ Call TASK METHODS independently and track success """ fault = False faults = [] - print('') + print('Initializing...') - if fault is False: + if not fault: fault = not createoutputdirectory() - if fault is True: + if fault: faults.append('createoutputdirectory()') - if fault is False: + if not fault: fault = not opendebugfile() - if fault is True: + if fault: faults.append('opendebugfile()') - if fault is False: + if not fault: + fault = not openundefinedfile() + + if fault: + faults.append('openundefinedfile()') + + if not fault: print('Loading source data...') - if fault is False: + if not fault: fault = not loadclientopcodes() - if fault is True: + if fault: faults.append('loadclientopcodes()') - if fault is False: + if not fault: fault = not loadserveropcodes() - if fault is True: + if fault: faults.append('loadserveropcodes()') - if fault is False: + if not fault: fault = not loadclienttranslators() - if fault is True: + if fault: faults.append('loadclienttranslators()') - if fault is False: + if not fault: fault = not loadserverhandlers() - if fault is True: + if fault: faults.append('loadserverhandlers()') - if fault is False: + if not fault: fault = not discoverserverhandlers() - if fault is True: + if fault: faults.append('discoverserverhandlers()') - if fault is False: + if not fault: fault = not clearemptyserverentries() - if fault is True: + if fault: faults.append('clearemptyserverentries()') - if fault is False: + if not fault: print('Creating output streams...') - if fault is False: + if not fault: fault = not openoutputfiles() - if fault is True: + if fault: faults.append('openoutputfiles()') - if fault is False: + if not fault: print('Parsing opcode data...') - if fault is False: + if not fault: fault = not parseclientopcodedata() - if fault is True: + if fault: faults.append('parseclientopcodedata()') - if fault is False: + if not fault: fault = not parseserveropcodedata() - if fault is True: + if fault: faults.append('parseserveropcodedata()') - if fault is False: + if not fault: print('Destroying output streams...') - # these should always be processed..verbose or silent if not closeoutputfiles(): + fault = True faults.append('closeoutputfiles()') if not closedebugfile(): + fault = True faults.append('closedebugfile()') - if len(faults) > 0: + if fault and len(faults) > 0: message = 'Script failed due to errors in:\n' for entry in faults: - message += ' {0}'.format(entry) + message += ' {0}\n'.format(entry) print(message) @@ -138,58 +152,119 @@ def main(): def createoutputdirectory(): - """ Check for output directory - create if does not exist """ + """ Check for OUTPUT DIRECTORY - create if does not exist """ try: output_path = '{0}/utils/scripts/opcode_handlers_output'.format(base_path) - if DEBUG >= 1: - print(output_path) + vprint(output_path) if not os.path.exists(output_path): os.mkdir(output_path) return True except: - if DEBUG >= 2: - print('EXCEPTION ERROR->createoutputdirectory({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) createoutputdirectory()'.format(sys.exc_info()[0])) return False def opendebugfile(): - file_name = '{0}/utils/scripts/opcode_handlers_output/DEBUG.txt'.format(base_path) + """ DEBUG FILE should always open """ - if DEBUG >= 1: - print(file_name) + try: + file_name = '{0}/utils/scripts/opcode_handlers_output/DEBUG.txt'.format(base_path) - out_files['DEBUG'] = open(file_name, 'w') + vprint(file_name) - return True + out_files['DEBUG'] = open(file_name, 'w') + + dprint( + '>> \'Opcode-Handler\' DEBUG dump file\n' + '>> file generated @ {0}\n\n' + '->open: \'{1}\' in \'w\' mode\n' + 'leaving \'opendebugfile()\'\n\n'.format( + ctime(time()), + file_name + ) + ) + + return True + except: + print('(Exception Error: {0}) opendebugfile()'.format(sys.exc_info()[0])) + + if 'DEBUG' in out_files: + vprint('Closing DEBUG output file...') + + out_files['DEBUG'].close() + + del out_files['DEBUG'] + + return False + + +def openundefinedfile(): + """ UNDEFINED FILE should always open """ + + dprint('entering \'openundefinedfile()\'\n') + + try: + file_name = '{0}/utils/scripts/opcode_handlers_output/UNDEFINED.txt'.format(base_path) + + vprint(file_name) + + out_files['UNDEFINED'] = open(file_name, 'w') + + uprint( + '>> \'Opcode-Handler\' UNDEFINED dump file\n' + '>> file generated @ {0}\n\n'.format(ctime(time())) + ) + + dprint( + '->open: \'{0}\' in \'w\' mode\n' + 'leaving \'openundefinedfile()\'\n\n'.format(file_name) + ) + + return True + except: + print('(Exception Error: {0}) openundefinedfile()'.format(sys.exc_info()[0])) + + if 'UNDEFINED' in out_files: + vprint('Closing UNDEFINED output file...') + + out_files['UNDEFINED'].close() + + del out_files['UNDEFINED'] + + return False def loadclientopcodes(): + """ Load CLIENT OPCODES into memory """ + + dprint('entering \'loadclientopcodes()\'\n') + bad_clients = [] for client in client_list: try: - short_name = '{0}{1}{2}'.format( - '/patch_', - client, - '.conf') + short_name = '/patch_{0}.conf'.format(client) - file_name = '{0}{1}{2}'.format( + file_name = '{0}/utils/patches{1}'.format( base_path, - '/utils/patches', - short_name) + short_name + ) - if DEBUG >= 1: - print(file_name) + vprint(file_name) with open(file_name, 'r') as data_file: - client_opcodes[client] = {} # force empty dictionary to avoid collisions + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + + client_opcodes[client] = {} + line_no = 0 for data_line in data_file: + line_no += 1 key_begin = data_line.find('OP_') key_end = data_line.find('=', key_begin) @@ -197,7 +272,7 @@ def loadclientopcodes(): continue val_begin = data_line.find('0x', key_end) - val_end = val_begin + 6 # max size is always 6 bytes + val_end = val_begin + 6 if val_begin < 0: continue @@ -209,57 +284,89 @@ def loadclientopcodes(): client_opcodes[client][data_line[key_begin:key_end]] = '0x{0}'.format(hex(value)[2:].zfill(4)) - if DEBUG >= 2: - print('[{0}][{1}] = {2} (int: {3})'.format( - client, - data_line[key_begin:key_end], - client_opcodes[client][data_line[key_begin:key_end]], - value)) + dprint('../utils/patches{0}({1}:{2}) [{3}][{4}] = {5}\n'.format( + short_name, + line_no, + key_begin, + client, + data_line[key_begin:key_end], + client_opcodes[client][data_line[key_begin:key_end]] + )) data_file.close() + + dprint('->close: \'{0}\'\n'.format(file_name)) + + if not len(client_opcodes[client]) > 0: + bad_clients.append(client) except: - if DEBUG >= 2: - print('EXCEPTION ERROR->loadclientopcodes({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) loadclientopcodes() [{1}]'.format( + sys.exc_info()[0], + client + )) + + dprint('<-except: \'{0} [{1}]\'\n'.format( + sys.exc_info()[0], + client + )) bad_clients.append(client) for bad_client in bad_clients: - if DEBUG >= 1: - print('Deleting \'{0}\' client from search criteria...'.format(bad_client)) + vprint('Deleting \'{0}\' client from search criteria...'.format(bad_client)) client_list.remove(bad_client) - if DEBUG >= 1: - print('Deleting stale entries for \'{0}\' client...'.format(bad_client)) + dprint('->delete: \'{0}\' client\n'.format(bad_client)) if bad_client in client_opcodes: + vprint('Deleting stale entries for \'{0}\' client...'.format(bad_client)) + del client_opcodes[bad_client] - + + dprint('->delete: \'{0}\' client opcode entries\n'.format(bad_client)) + if not len(client_list) > 0: + print('Could not locate valid clients...') + + dprint('leaving \'loadclientopcodes(): NO VALID CLIENTS EXIST\'\n\n') + return False + if not len(client_opcodes) > 0: + print('Could not locate client opcode lists...') + + dprint('leaving \'loadclientopcodes(): CLIENT OPCODES NOT FOUND\'\n\n') + + return False + + dprint('leaving \'loadclientopcodes()\'\n\n') + return True def loadserveropcodes(): + """ Load SERVER OPCODES into memory """ + + dprint('entering \'loadserveropcodes()\'\n') + try: - value = 0 + server_opcodes['OP_Unknown'] = 0 + value = 1 - server_opcodes['OP_Unknown'] = value - value += 1 + dprint('(manual) \'Servers\' [OP_Unknown] = {0}\n'.format(server_opcodes['OP_Unknown'])) - if DEBUG >= 2: - print('N[Server](OP_Unknown) = {0}'.format(server_opcodes['OP_Unknown'])) + file_name = '{0}/common/emu_oplist.h'.format(base_path) - file_name = '{0}{1}'.format( - base_path, - '/common/emu_oplist.h') - - if DEBUG >= 1: - print(file_name) + vprint(file_name) with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + + line_no = 0 + for data_line in data_file: + line_no += 1 val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -270,23 +377,28 @@ def loadserveropcodes(): server_opcodes[data_line[val_begin:val_end]] = value value += 1 - if DEBUG >= 2: - print('N[{0}]({1}) = {2}'.format( - 'Server', - data_line[val_begin:val_end], - server_opcodes[data_line[val_begin:val_end]])) + dprint('../common/emu_oplist.h({0}:{1}) \'Servers\' [{2}] = {3}\n'.format( + line_no, + val_begin, + data_line[val_begin:val_end], + server_opcodes[data_line[val_begin:val_end]] + )) data_file.close() - file_name = '{0}{1}'.format( - base_path, - '/common/mail_oplist.h') + dprint('->close: \'{0}\'\n'.format(file_name)) - if DEBUG >= 1: - print(file_name) + file_name = '{0}/common/mail_oplist.h'.format(base_path) + + vprint(file_name) with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + + line_no = 0 + for data_line in data_file: + line_no += 1 val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -297,46 +409,72 @@ def loadserveropcodes(): server_opcodes[data_line[val_begin:val_end]] = value value += 1 - if DEBUG >= 2: - print('N[{0}]({1}) = {2}'.format( - 'Server', - data_line[val_begin:val_end], - server_opcodes[data_line[val_begin:val_end]])) + dprint('../common/mail_oplist.h({0}:{1}) \'Servers\' [{2}] = {3}\n'.format( + line_no, + val_begin, + data_line[val_begin:val_end], + server_opcodes[data_line[val_begin:val_end]] + )) data_file.close() + + dprint('->close: \'{0}\'\n'.format(file_name)) except: - if DEBUG >= 2: - print('EXCEPTION ERROR->loadserveropcodes({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) loadserveropcodes()'.format(sys.exc_info()[0])) + + dprint('leaving \'loadserveropcodes(): EXCEPTION ERROR\'\n\n') return False + if not len(server_opcodes) > 0: + print('Could not locate server opcode list...') + + dprint('leaving \'loadserveropcodes(): SERVER OPCODES NOT FOUND\'\n\n') + + return False + + dprint('leaving \'loadserveropcodes()\'\n\n') + return True def loadclienttranslators(): + """ + Load CLIENT ENCODES and CLIENT DECODES OPCODES into memory + + The CLIENT LIST should be clean of any invalid entries by the time this + function is called. Client translator load failures are only commented + upon in output streams and do not trigger a removal of the client. + + """ + + dprint('entering \'loadclienttranslators()\'\n') + + bad_clients = [] + for client in client_list: - if client == '6.2': - short_name = '{0}'.format('/Client62_ops.h') - else: - short_name = '{0}{1}{2}'.format( - '/', - client, - '_ops.h') - try: - file_name = '{0}{1}{2}'.format( + if client == '6.2': + short_name = '/Client62_ops.h' + else: + short_name = '/{0}_ops.h'.format(client) + + file_name = '{0}/common/patches{1}'.format( base_path, - '/common/patches', - short_name) + short_name + ) - if DEBUG >= 1: - print(file_name) + vprint(file_name) with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + client_encodes[client] = [] client_decodes[client] = [] + line_no = 0 for data_line in data_file: + line_no += 1 val_begin = data_line.find('OP_', 2) val_end = data_line.find(')', val_begin) @@ -345,159 +483,255 @@ def loadclienttranslators(): if data_line[:1] == 'E': client_encodes[client].append(data_line[val_begin:val_end]) - - if DEBUG >= 2: - print('E[{0}]({1}) (listed: {2})'.format( - client, - data_line[val_begin:val_end], - data_line[val_begin:val_end] in client_encodes[client])) + + dprint('..{0}({1}:{2}) \'ENCODE\' [{3}] = {4}\n'.format( + short_name, + line_no, + val_begin, + client, + data_line[val_begin:val_end] + )) elif data_line[:1] == 'D': client_decodes[client].append(data_line[val_begin:val_end]) - if DEBUG >= 2: - print('D[{0}]({1}) (listed: {2})'.format( - client, - data_line[val_begin:val_end], - data_line[val_begin:val_end] in client_decodes[client])) + dprint('..{0}({1}:{2}) \'DECODE\' [{3}] = {4}\n'.format( + short_name, + line_no, + val_begin, + client, + data_line[val_begin:val_end] + )) data_file.close() + + dprint('->close: \'{0}\'\n'.format(file_name)) except: - if DEBUG >= 2: - print('EXCEPTION ERROR->loadclienttranslators({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) loadclienttranslators() [{1}]'.format( + sys.exc_info()[0], + client + )) - return False + dprint('<-except: \'{0} [{1}]\'\n'.format( + sys.exc_info()[0], + client + )) + + bad_clients.append(client) + + for bad_client in bad_clients: + if bad_client in client_encodes or bad_client in client_decodes: + vprint('Deleting stale entries for \'{0}\' client...'.format(bad_client)) + + if bad_client in client_encodes: + del client_encodes[bad_client] + + dprint('->delete: \'{0}\' client encode entries\n'.format(bad_client)) + + if bad_client in client_decodes: + del client_decodes[bad_client] + + dprint('->delete: \'{0}\' client decode entries\n'.format(bad_client)) - # there's always going to be at least one client with one encode or decode if not len(client_encodes) > 0 and not len(client_decodes) > 0: + dprint('leaving \'loadclienttranslators(): NO CLIENT ENCODES OR DECODES FOUND\'\n\n') + return False + dprint('leaving \'loadclienttranslators()\'\n\n') + return True def loadserverhandlers(): - """ Load pre-designated SERVER opcode handlers """ + """ Load pre-designated SERVER OPCODE HANDLERS """ + + # TODO: handle remarked out definitions in file (i.e., // and /**/) + + dprint('entering \'loadserverhandlers()\'\n') - # TODO: handle remarked out definitions in file (i.e., // and /**/); bad_servers = [] for server in server_list: try: if server == 'Login': - if DEBUG >= 1: - print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + vprint('No pre-designated server opcode handlers for \'Login\'') + dprint('->pass: \'Login\' server\n') continue elif server == 'World': - if DEBUG >= 1: - print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + vprint('No pre-designated server opcode handlers for \'World\'') + dprint('->pass: \'World\' server\n') continue elif server == 'Zone': - file_name = '{0}{1}'.format( - base_path, - '/zone/client_packet.cpp') + file_name = '{0}/zone/client_packet.cpp'.format(base_path) - if DEBUG >= 1: - print(file_name) + vprint(file_name) with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + server_handlers[server] = {} - can_run = False + handler_assigns = {} + step_1 = False + step_2 = False line_no = 0 + hint = 'Near beginning of file' for data_line in data_file: line_no += 1 + read_begin = 0 - if can_run is False: + if step_1 is False: if data_line[:19] == 'void MapOpcodes() {': - can_run = True + step_1 = True continue - if data_line[0:1] == '}': - break + if step_2 is False: + if data_line[0:1] == '}': + step_2 = True + + continue + + val_begin = data_line.find('OP_', read_begin) + val_end = data_line.find(']', val_begin) - key_begin = data_line.find('OP_') - key_end = data_line.find(']', key_begin) + if val_begin < 0 or val_end < 0: + continue + + if not data_line[val_begin:val_end] in server_opcodes: + dprint('\nILLEGAL OPCODE FOUND: ../zone/client_packet.cpp({0}:{1}) \'{2}\'\n'.format( + line_no, + val_begin, + data_line[val_begin:val_end] + )) + + continue + + key_begin = data_line.find('Client::', val_end) + key_end = data_line.find(';', key_begin) - if key_begin < 0 or key_end < 0: - continue - - val_begin = data_line.find('Client::', key_end) - val_end = data_line.find(';', val_begin) - - if val_begin < 0 or val_end < 0: + if key_begin < 0 or key_end < 0: + continue + + if not data_line[key_begin:key_end] in handler_assigns: + handler_assigns[data_line[key_begin:key_end]] = data_line[val_begin:val_end] + continue - # TODO: add continue on 'in server_opcodes' failure + if data_line[:1].isalpha(): + hint_begin = 0 + hint_end = data_line.find('(') - if not data_line[key_begin:key_end] in server_handlers[server]: - server_handlers[server][data_line[key_begin:key_end]] = [] + if not hint_end < 0: + hint_begin = hint_end - 1 - server_handlers[server][data_line[key_begin:key_end]].append( - '../zone/client_packet.cpp({0}:{1}) \'{2}\''.format( + while not hint_begin < 0: + if data_line[(hint_begin - 1):hint_begin].isspace(): + if not data_line[hint_begin:(hint_begin + 1)].isalpha(): + hint_begin += 1 + + hint = '[RX] Near {0}'.format(data_line[hint_begin:hint_end]) + + break + + hint_begin -= 1 + else: + continue + + if hint[10:] in handler_assigns: + if not handler_assigns[hint[10:]] in server_handlers[server]: + server_handlers[server][handler_assigns[hint[10:]]] = [] + + server_handlers[server][handler_assigns[hint[10:]]].append( + '../zone/client_packet.cpp({0}:{1}) \'{2}\''.format( + line_no, + hint_begin, + hint + ) + ) + + dprint('../zone/client_packet.cpp({0}:{1}) [{2}][{3}] = \'{4}\'\n'.format( line_no, - key_begin, - data_line[val_begin:val_end])) - - if DEBUG >= 2: - print('[{0}][{1}]({2}) [{3}]'.format( + hint_begin, server, - data_line[key_begin:key_end], - data_line[val_begin:val_end], - data_line[val_begin:val_end] in server_handlers[server][data_line[key_begin:key_end]])) + handler_assigns[hint[10:]], + hint + )) + + del handler_assigns[hint[10:]] + + if len(handler_assigns) > 0: + for unhandled in handler_assigns: + dprint('\nUNMATCHED DESIGNATED HANDLER FOUND: ../zone/client_packet.cpp \'{0}\'\n'.format( + unhandled + )) data_file.close() + + dprint('->close: \'{0}\'\n'.format(file_name)) elif server == 'UCS': - if DEBUG >= 1: - print('No pre-designated server opcode handlers for \'{0}\''.format(server)) + vprint('No pre-designated server opcode handlers for \'UCS\'') + dprint('->pass: \'UCS\' server\n') continue else: - if DEBUG >= 1: - print('No pre-designated server opcode handlers for \'{0}\''.format(server)) - - if DEBUG >= 2: - print('->LoadServerHandlers(Someone added a new server and forgot to code for the data load...)') + vprint('No pre-designated server opcode handlers for \'{0}\''.format(server)) + dprint('->pass: \'{0}\' server\n'.format(server)) continue except: - if DEBUG >= 2: - print('EXCEPTION ERROR->loadserverhandlers({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) loadserverhandlers() [{1}]'.format( + sys.exc_info()[0], + server + )) + + dprint('<-except: \'{0} [{1}]\'\n'.format( + sys.exc_info()[0], + server + )) bad_servers.append(server) for bad_server in bad_servers: - if DEBUG >= 1: - print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) - - server_list.remove(bad_server) - - if DEBUG >= 1: - print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) - if bad_server in server_handlers: + vprint('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + del server_handlers[bad_server] - if not len(server_list) > 0: - return False + dprint('->delete: \'{0}\' server designated handler entries\n'.format(bad_server)) + + dprint('leaving \'loadserverhandlers()\'\n\n') return True def discoverserverhandlers(): - """ Load undefined SERVER opcode handlers using 'discovery' method """ + """ + Load undefined SERVER OPCODE HANDLERS using 'discovery' method + When adding new servers and/or search locations, use the following format: + + if 'Server' in locations: + locations['Server'].append('//.') + + Lists are instantiated for all SERVERS in SERVER LIST. The lists are then appended + with location data based on the presence of the SERVER in the parent dictionary. + + """ + + # TODO: handle remarked out definitions in file (i.e., // and /**/) + # TODO: if/how to include perl, lua and non-'..//' location handlers... + + dprint('entering \'discoverserverhandlers()\'\n') + + bad_servers = [] locations = {} - for server in server_list: # initialize lists for any remaining servers - locations[server] = [] - - # manually enter search locations - #if 'Server' in locations: - # locations['Server'].append('//.') - - # TODO: if/how to include perl/lua handlers... + for server in server_list: + if not server in locations: + locations[server] = [] if 'Login' in locations: locations['Login'].append('/loginserver/Client.cpp') @@ -507,8 +741,6 @@ def discoverserverhandlers(): if 'World' in locations: locations['World'].append('/world/client.cpp') - # the bulk of opcodes are handled in 'Zone' - if processing occurs on a different - # server, you will need to manually trace 'ServerPacket' to the deferred location if 'Zone' in locations: locations['Zone'].append('/zone/AA.cpp') locations['Zone'].append('/zone/attack.cpp') @@ -564,15 +796,17 @@ def discoverserverhandlers(): base_path, location) - if DEBUG >= 1: - print(file_name) + vprint(file_name) with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + line_no = 0 hint = 'Near beginning of file' for data_line in data_file: line_no += 1 + read_begin = 0 if data_line[:1].isalpha(): hint_end = data_line.find('(') @@ -591,28 +825,199 @@ def discoverserverhandlers(): hint_begin -= 1 - op_begin = data_line.find('OP_') + op_begin = data_line.find('OP_', read_begin) if op_begin < 0: continue if data_line[(op_begin - 20):op_begin] == 'EQApplicationPacket(': key_begin = op_begin - key_end = data_line.find(',', key_begin) + key_end = key_begin + 3 + direction = '[TX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 elif data_line[(op_begin - 12):op_begin] == '->SetOpcode(': key_begin = op_begin - key_end = data_line.find(')', key_begin) + key_end = key_begin + 3 + direction = '[TX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 elif data_line[(op_begin - 5):op_begin] == 'case ': key_begin = op_begin - key_end = data_line.find(':', key_begin) + key_end = key_begin + 3 + direction = '[RX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + elif data_line[(op_begin - 3):op_begin] == '== ': + key_begin = op_begin + key_end = key_begin + 3 + direction = '[RX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + elif data_line[(op_begin - 2):op_begin] == '==': + key_begin = op_begin + key_end = key_begin + 3 + direction = '[RX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + elif data_line[(op_begin - 3):op_begin] == '!= ': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 2):op_begin] == '!=': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 3):op_begin] == '>= ': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 2):op_begin] == '>=': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 3):op_begin] == '<= ': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 2):op_begin] == '<=': + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + dprint( + '\nILL-DEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + + continue + elif data_line[(op_begin - 2):op_begin] == '= ': + key_begin = op_begin + key_end = key_begin + 3 + direction = '[TX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + elif data_line[(op_begin - 1):op_begin] == '=': + key_begin = op_begin + key_end = key_begin + 3 + direction = '[TX]' + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 else: + key_begin = op_begin + key_end = key_begin + 3 + + while data_line[key_end:(key_end + 1)].isalnum(): + key_end += 1 + + uprint( + '\nUNDEFINED OPCODE CONDITIONAL FOUND: ..{0}({1}:{2}) \'{3}\'\n' + '->line: \'{4}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end], + data_line[:-1] + ) + ) + continue if key_end < 0: continue if not data_line[key_begin:key_end] in server_opcodes: - out_files['DEBUG'].write('Illegal Opcode Found: ..{0} ({1}:{2}) \'{3}\'\n'.format( + dprint('\nILLEGAL OPCODE FOUND: ..{0}({1}:{2}) \'{3}\'\n'.format( location, line_no, key_begin, @@ -624,25 +1029,77 @@ def discoverserverhandlers(): if not data_line[key_begin:key_end] in server_handlers[server]: server_handlers[server][data_line[key_begin:key_end]] = [] - if not data_line in server_handlers[server][data_line[key_begin:key_end]]: - server_handlers[server][data_line[key_begin:key_end]].append( - '..{0}({1}:{2}) \'{3}\''.format( - location, - line_no, - key_begin, - hint)) + server_handlers[server][data_line[key_begin:key_end]].append( + '..{0}({1}:{2}) \'{3}\''.format( + location, + line_no, + key_begin, + '{0} {1}'.format( + direction, + hint + ) + ) + ) + + dprint('..{0}({1}:{2}) [{3}][{4}] = \'{5} {6}\'\n'.format( + location, + line_no, + key_begin, + server, + data_line[key_begin:key_end], + direction, + hint + )) + + data_file.close() + + dprint('->close: \'{0}\'\n'.format(file_name)) except: - if DEBUG >= 2: - print('EXCEPTION ERROR->discoverserverhandlers({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) discoverserverhandlers() [{1}]'.format( + sys.exc_info()[0], + server + )) + + dprint('<-except: \'{0} [{1}]\'\n'.format( + sys.exc_info()[0], + server + )) + + if not server in bad_servers: + bad_servers.append(server) + + for bad_server in bad_servers: + if bad_server in server_handlers: + vprint('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + + del server_handlers[bad_server] + + dprint('->delete: \'{0}\' server discovered handler entries\n'.format(bad_server)) + + dprint('leaving \'discoverserverhandlers()\'\n\n') return True def clearemptyserverentries(): + """ + Delete SERVER OPCODE HANDLERS with no references + + Server methods are treated a little differently than client ones. Because of multiple + search functions, we do not want to invalidate a server until all data-load processes + are performed. + + This function cleans up and removes unused server references after all relevant + processing is complete. + + """ + + dprint('entering \'clearemptyserverentries()\'\n') + bad_servers = [] for server in server_list: - if len(server_handlers[server]) == 0: + if not server in server_handlers or not len(server_handlers[server]) > 0: bad_servers.append(server) else: bad_opcodes = [] @@ -658,108 +1115,138 @@ def clearemptyserverentries(): bad_servers.append(server) for bad_server in bad_servers: - if DEBUG >= 1: - print('Deleting \'{0}\' server from search criteria...'.format(bad_server)) - print('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + vprint('Deleting \'{0}\' server from search criteria...'.format(bad_server)) - del server_handlers[bad_server] server_list.remove(bad_server) + dprint('->delete: \'{0}\' server\n'.format(bad_server)) + + if bad_server in server_handlers: + vprint('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + + del server_handlers[bad_server] + + dprint('->delete: \'{0}\' server handler entries\n'.format(bad_server)) + + if not len(server_list) > 0: + print('Could not locate valid servers...') + + dprint('leaving \'clearemptyserverentries(): NO VALID SERVERS EXIST\'\n\n') + + return False + + if not len(server_handlers) > 0: + print('Could not locate server handlers...') + + dprint('leaving \'clearemptyserverentries(): SERVER HANDLERS NOT FOUND\'\n\n') + + return False + + dprint('leaving \'clearemptyserverentries()\'\n\n') + return True def openoutputfiles(): - """ Open output files in 'w' mode - create/overwrite mode """ + """ Open OUTPUT FILES in 'w' mode - create/overwrite mode """ + + dprint('entering \'openoutputfiles()\'\n') try: - file_name = '{0}/utils/scripts/opcode_handlers_output/Report.txt'.format(base_path) + file_name = '{0}/utils/scripts/opcode_handlers_output/REPORT.txt'.format(base_path) - if DEBUG >= 1: - print(file_name) + vprint(file_name) - out_files['Report'] = open(file_name, 'w') + out_files['REPORT'] = open(file_name, 'w') + + dprint('->open: \'{0}\' in \'w\' mode\n'.format(file_name)) + + rprint( + '>> \'Opcode-Handler\' REPORT file\n' + '>> file generated @ {0}\n\n'.format(ctime(time())) + ) for client in client_list: - file_name = '{0}{1}{2}{3}'.format( + file_name = '{0}/utils/scripts/opcode_handlers_output/{1}_opcode_handlers.txt'.format( base_path, - '/utils/scripts/opcode_handlers_output/', - client, - '_opcode_handlers.txt') + client + ) - if DEBUG >= 1: - print(file_name) + vprint(file_name) out_files[client] = open(file_name, 'w') - message = \ - '>> \'Opcode-Handler\' analysis for \'{0}\' client\n' \ - '>> file generated @ {1}\n' \ - '\n'.format( + dprint('->open: \'{0}\' in \'w\' mode\n'.format(file_name)) + + cprint( + client, + '>> \'Opcode-Handler\' analysis for \'{0}\' client\n' + '>> file generated @ {1}\n\n'.format( client, - ctime(time())) - - out_files[client].write(message) - - if DEBUG >= 2: - print(message[:-2]) + ctime(time()) + ) + ) for server in server_list: - file_name = '{0}{1}{2}{3}'.format( + file_name = '{0}/utils/scripts/opcode_handlers_output/{1}_opcode_handlers.txt'.format( base_path, - '/utils/scripts/opcode_handlers_output/', - server, - '_opcode_handlers.txt') + server + ) - if DEBUG >= 1: - print(file_name) + vprint(file_name) out_files[server] = open(file_name, 'w') - message = \ - '>> \'Opcode-Handler\' analysis for \'{0}\' server\n' \ - '>> file generated @ {1}\n' \ - '\n'.format( + dprint('->open: \'{0}\' in \'w\' mode\n'.format(file_name)) + + sprint( + server, + '>> \'Opcode-Handler\' analysis for \'{0}\' server\n' + '>> file generated @ {1}\n\n'.format( server, - ctime(time())) + ctime(time()) + ) + ) - out_files[server].write(message) + dprint('leaving \'openoutputfiles()\'\n\n') - if DEBUG >= 2: - print(message[:-2]) + return True except: - if DEBUG >= 2: - print('EXCEPTION ERROR->openoutputfiles({0})'.format(sys.exc_info()[0])) + print('(Exception Error: {0}) openoutputfiles()'.format(sys.exc_info()[0])) + + if 'REPORT' in out_files: + vprint('Closing REPORT output file...') + + out_files['REPORT'].close() + + del out_files['REPORT'] for client in client_list: if client in out_files: + vprint('Closing {0} client output file...'.format(client)) + out_files[client].close() + del out_files[client] - if DEBUG >= 2: - print('->OpeningClientStream(exception): {0}'.format(client)) - for server in server_list: if server in out_files: + vprint('Closing {0} server output file...'.format(server)) + out_files[server].close() + del out_files[server] - if DEBUG >= 2: - print('->OpeningServerStream(exception): {0}'.format(server)) - - if 'Report' in out_files: - out_files['Report'].close() - del out_files['Report'] - - if DEBUG >= 2: - print('->OpeningReportStream(exception)') + dprint('leaving \'openoutputfiles(): EXCEPTION ERROR\'\n\n') return False - return True - def parseclientopcodedata(): - # TODO: add metrics + """ Process CLIENT OPCODE cross-link references """ + + dprint('entering \'parseclientopcodedata()\'\n') + for client in client_list: server_max_len = 0 @@ -785,7 +1272,8 @@ def parseclientopcodedata(): client_opcodes[client][client_opcode], handled, encoded, - decoded) + decoded + ) for server in server_list: if client_opcode in server_handlers[server] and len(server_handlers[server][client_opcode]) > 0: @@ -796,30 +1284,29 @@ def parseclientopcodedata(): message += ' Server: {0} ({1}) | Handler: {2}\n'.format( server.ljust(len(server) + (server_max_len - len(server)), ' '), '{0}'.format(server_opcodes[client_opcode]).zfill(4), - handler_entry) + handler_entry + ) else: message += ' Server: {0} (0000) | Handler: N/A\n'.format( - server.ljust(len(server) + (server_max_len - len(server)), ' ')) - - if DEBUG >= 2: - print('->EndOfServerLoop: {0}'.format(server)) + server.ljust(len(server) + (server_max_len - len(server)), ' ') + ) message += '\n' - out_files[client].write(message) + cprint(client, message) - if DEBUG >= 2: - print(message[:-2]) - print('->EndOfOpcodeLoop: {0}'.format(client_opcode)) + dprint('->parse: \'{0}\' client\n'.format(client)) - if DEBUG >= 2: - print('->EndOfClientLoop: {0}'.format(client)) + dprint('leaving \'parseclientopcodedata()\'\n\n') return True def parseserveropcodedata(): - # TODO: add metrics + """ Process SERVER OPCODE cross-link references """ + + dprint('entering \'parseserveropcodedata()\'\n') + for server in server_list: client_max_len = 0 @@ -839,7 +1326,7 @@ def parseserveropcodedata(): for handler_entry in handler_list: message += 'Opcode: {0} ({1}) | Handler: {2}\n'.format( handler_opcode, - server_opcodes[handler_opcode], + '{0}'.format(server_opcodes[handler_opcode]).zfill(4), handler_entry) for client in client_list: @@ -859,62 +1346,126 @@ def parseserveropcodedata(): val1, val2.ljust(len(val2) + (len('False') - len(val2)), ' '), val3.ljust(len(val3) + (len('False') - len(val3)), ' '), - val4.ljust(len(val4) + (len('False') - len(val4)), ' ')) - - if DEBUG >= 2: - print('->EndOfClientLoop: {0}'.format(client)) + val4.ljust(len(val4) + (len('False') - len(val4)), ' ') + ) message += '\n' - out_files[server].write(message) + sprint(server, message) - if DEBUG >= 2: - print(message[:-2]) - print('->EndOfOpcodeLoop: {0}'.format(handler_opcode)) + dprint('->parse: \'{0}\' server\n'.format(server)) - if DEBUG >= 2: - print('->EndOfServerLoop: {0}'.format(server)) + dprint('leaving \'parseserveropcodedata()\'\n\n') return True def closeoutputfiles(): + """ Close OUTPUT FILES - excluding DEBUG FILE """ + + dprint('entering \'closeoutputfiles()\'\n') + + if 'REPORT' in out_files: + file_name = out_files['REPORT'].name + + out_files['REPORT'].close() + + del out_files['REPORT'] + + dprint('->close: \'{0}\'\n'.format(file_name)) + + if 'UNDEFINED' in out_files: + file_name = out_files['UNDEFINED'].name + + out_files['UNDEFINED'].close() + + del out_files['UNDEFINED'] + + dprint('->close: \'{0}\'\n'.format(file_name)) + for client in client_list: if client in out_files: + file_name = out_files[client].name + out_files[client].close() + del out_files[client] - if DEBUG >= 2: - print('->ClosingClientStream: {0}'.format(client)) + dprint('->close: \'{0}\'\n'.format(file_name)) for server in server_list: if server in out_files: + file_name = out_files[server].name + out_files[server].close() + del out_files[server] - if DEBUG >= 2: - print('->ClosingServerStream: {0}'.format(server)) + dprint('->close: \'{0}\'\n'.format(file_name)) - if 'Report' in out_files: - out_files['Report'].close() - del out_files['Report'] - - if DEBUG >= 2: - print('->ClosingReportStream') + dprint('leaving \'closeoutputfiles()\'\n\n') return True def closedebugfile(): - if 'DEBUG' in out_files: - out_files['DEBUG'].close() - del out_files['DEBUG'] + """ Close DEBUG FILE - last performed action to catch late messages """ - if DEBUG >= 2: - print('->ClosingDEBUGStream') + dprint('entering \'closedebugfile()\'\n') + + if 'DEBUG' in out_files: + file_name = out_files['DEBUG'].name + + dprint('closing \'{0}\'\n'.format(file_name)) + + out_files['DEBUG'].close() + + del out_files['DEBUG'] return True +def cprint(client, message): + """ CLIENT PRINT helper function """ + + if client in out_files: + out_files[client].write(message) + + +def dprint(message): + """ DEBUG PRINT helper function """ + + if 'DEBUG' in out_files: + out_files['DEBUG'].write(message) + + +def rprint(message): + """ REPORT PRINT helper function """ + + if 'REPORT' in out_files: + out_files['REPORT'].write(message) + + +def sprint(server, message): + """ SERVER PRINT helper function """ + + if server in out_files: + out_files[server].write(message) + + +def uprint(message): + """ UNDEFINED PRINT helper function """ + + if 'UNDEFINED' in out_files: + out_files['UNDEFINED'].write(message) + + +def vprint(message): + """ VERBOSE PRINT helper function """ + + if VERBOSE: + print(message) + + if __name__ == '__main__': main() diff --git a/utils/sql/git/optional/2014_08_12_raid_timer_rule.sql b/utils/sql/git/optional/2014_08_12_raid_timer_rule.sql new file mode 100644 index 000000000..46f5acb37 --- /dev/null +++ b/utils/sql/git/optional/2014_08_12_raid_timer_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:RestRegenRaidTimeToActivate', 300, 'Time in seconds for rest state regen to kick in with a raid target.'); diff --git a/utils/sql/git/required/2014_06_25_AA_Updates..sql b/utils/sql/git/required/2014_06_25_AA_Updates..sql new file mode 100644 index 000000000..3d7a048b6 --- /dev/null +++ b/utils/sql/git/required/2014_06_25_AA_Updates..sql @@ -0,0 +1,34 @@ +-- AA MGB update +UPDATE altadv_vars SET spellid = 5228 WHERE skill_id = 128; +UPDATE aa_actions SET spell_id = 5228, nonspell_action = 0 WHERE aaid = 128; + +-- AA Project Illusion update +UPDATE altadv_vars SET spellid = 5227 WHERE skill_id = 643; +UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643; + +-- AA Improved Reclaim Energy +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0'); + +-- AA Headshot +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0'); + +-- AA Anatomy (Rogue Assassinate) +INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0'); + +-- AA Finishing Blow Fix +DELETE FROM aa_effects WHERE aaid = 199 AND slot = 2; +DELETE FROM aa_effects WHERE aaid = 200 AND slot = 2; +DELETE FROM aa_effects WHERE aaid = 201 AND slot = 2; +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '1', '278', '500', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '2', '440', '50', '200'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '1', '278', '500', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '2', '440', '52', '200'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '1', '278', '500', '32000'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '2', '440', '54', '200'); \ No newline at end of file diff --git a/utils/sql/git/required/2014_08_12_NPC_raid_targets.sql b/utils/sql/git/required/2014_08_12_NPC_raid_targets.sql new file mode 100644 index 000000000..a91a02815 --- /dev/null +++ b/utils/sql/git/required/2014_08_12_NPC_raid_targets.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD `raid_target` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `no_target_hotkey`; diff --git a/world/world_logsys.cpp b/world/world_logsys.cpp index f60186a86..d7f798b6c 100644 --- a/world/world_logsys.cpp +++ b/world/world_logsys.cpp @@ -12,8 +12,7 @@ void log_message_clientVA(LogType type, Client *who, const char *fmt, va_list args) { - std::string prefix_buffer; - StringFormat(prefix_buffer,"[%s] %s: ", log_type_info[type].name, who->GetAccountName()); + std::string prefix_buffer = StringFormat("[%s] %s: ", log_type_info[type].name, who->GetAccountName()); LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args); } @@ -31,11 +30,11 @@ void log_message_zoneVA(LogType type, ZoneServer *who, const char *fmt, va_list const char *zone_name=who->GetZoneName(); if (zone_name == nullptr) - StringFormat(zone_tag,"[%d]", who->GetID()); + zone_tag = StringFormat("[%d]", who->GetID()); else - StringFormat(zone_tag,"[%d] [%s]",who->GetID(),zone_name); + zone_tag = StringFormat("[%d] [%s]",who->GetID(),zone_name); - StringFormat(prefix_buffer, "[%s] %s ", log_type_info[type].name, zone_tag.c_str()); + prefix_buffer = StringFormat("[%s] %s ", log_type_info[type].name, zone_tag.c_str()); LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args); } diff --git a/zone/bot.cpp b/zone/bot.cpp index eef5f803f..aa5ab33f0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4303,8 +4303,9 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Get the zone id this bot spawned in _lastZoneId = GetZoneID(); - this->helmtexture = 0xFF; - this->texture = 0xFF; + // this change propagates to Bot::FillSpawnStruct() + this->helmtexture = 0; //0xFF; + this->texture = 0; //0xFF; if(this->Save()) this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName()); @@ -4329,18 +4330,18 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { this->SendPosition(); - /* // fillspawnstruct now properly handles this -U + // there is something askew with spawn struct appearance fields... + // I re-enabled this until I can sort it out -U uint32 itemID = 0; uint8 materialFromSlot = 0xFF; - for(int i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::EQUIPMENT_END; ++i) { + for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { itemID = GetBotItemBySlot(i); if(itemID != 0) { materialFromSlot = Inventory::CalcMaterialFromSlot(i); - if(materialFromSlot != 0xFF) { + if(materialFromSlot != 0xFF) this->SendWearChange(materialFromSlot); - } } - }*/ + } } } @@ -4586,8 +4587,8 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.size = 0; ns->spawn.NPC = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse - ns->spawn.helm = 0xFF; - ns->spawn.equip_chest2 = 0xFF; + ns->spawn.helm = helmtexture; //0xFF; + ns->spawn.equip_chest2 = texture; //0xFF; const Item_Struct* item = 0; const ItemInst* inst = 0; diff --git a/zone/client.cpp b/zone/client.cpp index 6b7828f1d..aa93478ba 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -324,6 +324,9 @@ Client::Client(EQStreamInterface* ieqs) initial_respawn_selection = 0; alternate_currency_loaded = false; + + EngagedRaidTarget = false; + SavedRaidRestTimer = 0; } Client::~Client() { @@ -4330,12 +4333,16 @@ void Client::IncrementAggroCount() { if(!RuleI(Character, RestRegenPercent)) return; - + // If we already had aggro before this method was called, the combat indicator should already be up for SoF clients, // so we don't need to send it again. // if(AggroCount > 1) return; + + // Pause the rest timer + if (AggroCount == 1) + SavedRaidRestTimer = rest_timer.GetRemainingTime(); if(GetClientVersion() >= EQClientSoF) { @@ -4367,14 +4374,27 @@ void Client::DecrementAggroCount() { // Something else is still aggro on us, can't rest yet. if(AggroCount) return; - rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - + uint32 time_until_rest; + if (GetEngagedRaidTarget()) { + time_until_rest = RuleI(Character, RestRegenRaidTimeToActivate) * 1000; + SetEngagedRaidTarget(false); + } else { + if (SavedRaidRestTimer > (RuleI(Character, RestRegenTimeToActivate) * 1000)) { + time_until_rest = SavedRaidRestTimer; + SavedRaidRestTimer = 0; + } else { + time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000; + } + } + + rest_timer.Start(time_until_rest); + if(GetClientVersion() >= EQClientSoF) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); char *Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RestRegenTimeToActivate)); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, (uint32)(time_until_rest / 1000)); QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/client.h b/zone/client.h index cce84673c..718efbb6e 100644 --- a/zone/client.h +++ b/zone/client.h @@ -789,8 +789,8 @@ public: void SetTint(int16 slot_id, Color_Struct& color); void SetMaterial(int16 slot_id, uint32 item_id); void Undye(); - uint32 GetItemIDAt(int16 slot_id); - uint32 GetAugmentIDAt(int16 slot_id, uint8 augslot); + int32 GetItemIDAt(int16 slot_id); + int32 GetAugmentIDAt(int16 slot_id, uint8 augslot); bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false); bool PushItemOnCursor(const ItemInst& inst, bool client_update = false); void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); @@ -1198,6 +1198,9 @@ public: int mod_food_value(const Item_Struct *item, int change); int mod_drink_value(const Item_Struct *item, int change); + void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; } + bool GetEngagedRaidTarget() const { return EngagedRaidTarget; } + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1442,6 +1445,9 @@ private: unsigned int RestRegenHP; unsigned int RestRegenMana; unsigned int RestRegenEndurance; + + bool EngagedRaidTarget; + uint32 SavedRaidRestTimer; std::set zone_flags; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c5254352c..960457125 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1248,7 +1248,6 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) delta_y = ppu->delta_y; delta_z = ppu->delta_z; delta_heading = ppu->delta_heading; - heading = EQ19toFloat(ppu->heading); if(IsTracking() && ((x_pos!=ppu->x_pos) || (y_pos!=ppu->y_pos))){ if(MakeRandomFloat(0, 100) < 70)//should be good @@ -1274,12 +1273,15 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) } // Outgoing client packet - if (ppu->y_pos != y_pos || ppu->x_pos != x_pos || ppu->heading != heading || ppu->animation != animation) + float tmpheading = EQ19toFloat(ppu->heading); + + if (!FCMP(ppu->y_pos, y_pos) || !FCMP(ppu->x_pos, x_pos) || !FCMP(tmpheading, heading) || ppu->animation != animation) { x_pos = ppu->x_pos; y_pos = ppu->y_pos; z_pos = ppu->z_pos; animation = ppu->animation; + heading = tmpheading; EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct* ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; @@ -9157,7 +9159,8 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - if(m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) + // 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; //This checksum should disappear once dynamic structs are in... each struct strategy will do it diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 1b585ca32..2d7bd3ea6 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -198,8 +198,11 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd list.push_back(p); parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "1", 0); - if(ent->IsClient()) + if (ent->IsClient()) { + if (owner->CastToNPC()->IsRaidTarget()) + ent->CastToClient()->SetEngagedRaidTarget(true); ent->CastToClient()->IncrementAggroCount(); + } } } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 969065fdb..9a815c6ca 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -659,7 +659,7 @@ void Client::DropInst(const ItemInst* inst) } // Returns a slot's item ID (returns INVALID_ID if not found) -uint32 Client::GetItemIDAt(int16 slot_id) { +int32 Client::GetItemIDAt(int16 slot_id) { const ItemInst* inst = m_inv[slot_id]; if (inst) return inst->GetItem()->ID; @@ -670,7 +670,7 @@ uint32 Client::GetItemIDAt(int16 slot_id) { // Returns an augment's ID that's in an item (returns INVALID_ID if not found) // Pass in the slot ID of the item and which augslot you want to check (0-4) -uint32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { +int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { const ItemInst* inst = m_inv[slot_id]; if (inst) if (inst->GetAugmentItemID(augslot)) diff --git a/zone/mob.cpp b/zone/mob.cpp index 97befb7fa..4816abbae 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3551,7 +3551,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) } } -uint32 Mob::GetItemStat(uint32 itemid, const char *identifier) +int32 Mob::GetItemStat(uint32 itemid, const char *identifier) { const ItemInst* inst = database.CreateItem(itemid); if (!inst) @@ -3564,7 +3564,7 @@ uint32 Mob::GetItemStat(uint32 itemid, const char *identifier) if (!identifier) return 0; - uint32 stat = 0; + int32 stat = 0; std::string id = identifier; for(int i = 0; i < id.length(); ++i) @@ -3573,316 +3573,318 @@ uint32 Mob::GetItemStat(uint32 itemid, const char *identifier) } if (id == "itemclass") - stat = uint32(item->ItemClass); + stat = int32(item->ItemClass); if (id == "id") - stat = uint32(item->ID); + stat = int32(item->ID); + if (id == "idfile") + stat = atoi(&item->IDFile[2]); if (id == "weight") - stat = uint32(item->Weight); + stat = int32(item->Weight); if (id == "norent") - stat = uint32(item->NoRent); + stat = int32(item->NoRent); if (id == "nodrop") - stat = uint32(item->NoDrop); + stat = int32(item->NoDrop); if (id == "size") - stat = uint32(item->Size); + stat = int32(item->Size); if (id == "slots") - stat = uint32(item->Slots); + stat = int32(item->Slots); if (id == "price") - stat = uint32(item->Price); + stat = int32(item->Price); if (id == "icon") - stat = uint32(item->Icon); + stat = int32(item->Icon); if (id == "loregroup") - stat = uint32(item->LoreGroup); + stat = int32(item->LoreGroup); if (id == "loreflag") - stat = uint32(item->LoreFlag); + stat = int32(item->LoreFlag); if (id == "pendingloreflag") - stat = uint32(item->PendingLoreFlag); + stat = int32(item->PendingLoreFlag); if (id == "artifactflag") - stat = uint32(item->ArtifactFlag); + stat = int32(item->ArtifactFlag); if (id == "summonedflag") - stat = uint32(item->SummonedFlag); + stat = int32(item->SummonedFlag); if (id == "fvnodrop") - stat = uint32(item->FVNoDrop); + stat = int32(item->FVNoDrop); if (id == "favor") - stat = uint32(item->Favor); + stat = int32(item->Favor); if (id == "guildfavor") - stat = uint32(item->GuildFavor); + stat = int32(item->GuildFavor); if (id == "pointtype") - stat = uint32(item->PointType); + stat = int32(item->PointType); if (id == "bagtype") - stat = uint32(item->BagType); + stat = int32(item->BagType); if (id == "bagslots") - stat = uint32(item->BagSlots); + stat = int32(item->BagSlots); if (id == "bagsize") - stat = uint32(item->BagSize); + stat = int32(item->BagSize); if (id == "bagwr") - stat = uint32(item->BagWR); + stat = int32(item->BagWR); if (id == "benefitflag") - stat = uint32(item->BenefitFlag); + stat = int32(item->BenefitFlag); if (id == "tradeskills") - stat = uint32(item->Tradeskills); + stat = int32(item->Tradeskills); if (id == "cr") - stat = uint32(item->CR); + stat = int32(item->CR); if (id == "dr") - stat = uint32(item->DR); + stat = int32(item->DR); if (id == "pr") - stat = uint32(item->PR); + stat = int32(item->PR); if (id == "mr") - stat = uint32(item->MR); + stat = int32(item->MR); if (id == "fr") - stat = uint32(item->FR); + stat = int32(item->FR); if (id == "astr") - stat = uint32(item->AStr); + stat = int32(item->AStr); if (id == "asta") - stat = uint32(item->ASta); + stat = int32(item->ASta); if (id == "aagi") - stat = uint32(item->AAgi); + stat = int32(item->AAgi); if (id == "adex") - stat = uint32(item->ADex); + stat = int32(item->ADex); if (id == "acha") - stat = uint32(item->ACha); + stat = int32(item->ACha); if (id == "aint") - stat = uint32(item->AInt); + stat = int32(item->AInt); if (id == "awis") - stat = uint32(item->AWis); + stat = int32(item->AWis); if (id == "hp") - stat = uint32(item->HP); + stat = int32(item->HP); if (id == "mana") - stat = uint32(item->Mana); + stat = int32(item->Mana); if (id == "ac") - stat = uint32(item->AC); + stat = int32(item->AC); if (id == "deity") - stat = uint32(item->Deity); + stat = int32(item->Deity); if (id == "skillmodvalue") - stat = uint32(item->SkillModValue); + stat = int32(item->SkillModValue); if (id == "skillmodtype") - stat = uint32(item->SkillModType); + stat = int32(item->SkillModType); if (id == "banedmgrace") - stat = uint32(item->BaneDmgRace); + stat = int32(item->BaneDmgRace); if (id == "banedmgamt") - stat = uint32(item->BaneDmgAmt); + stat = int32(item->BaneDmgAmt); if (id == "banedmgbody") - stat = uint32(item->BaneDmgBody); + stat = int32(item->BaneDmgBody); if (id == "magic") - stat = uint32(item->Magic); + stat = int32(item->Magic); if (id == "casttime_") - stat = uint32(item->CastTime_); + stat = int32(item->CastTime_); if (id == "reqlevel") - stat = uint32(item->ReqLevel); + stat = int32(item->ReqLevel); if (id == "bardtype") - stat = uint32(item->BardType); + stat = int32(item->BardType); if (id == "bardvalue") - stat = uint32(item->BardValue); + stat = int32(item->BardValue); if (id == "light") - stat = uint32(item->Light); + stat = int32(item->Light); if (id == "delay") - stat = uint32(item->Delay); + stat = int32(item->Delay); if (id == "reclevel") - stat = uint32(item->RecLevel); + stat = int32(item->RecLevel); if (id == "recskill") - stat = uint32(item->RecSkill); + stat = int32(item->RecSkill); if (id == "elemdmgtype") - stat = uint32(item->ElemDmgType); + stat = int32(item->ElemDmgType); if (id == "elemdmgamt") - stat = uint32(item->ElemDmgAmt); + stat = int32(item->ElemDmgAmt); if (id == "range") - stat = uint32(item->Range); + stat = int32(item->Range); if (id == "damage") - stat = uint32(item->Damage); + stat = int32(item->Damage); if (id == "color") - stat = uint32(item->Color); + stat = int32(item->Color); if (id == "classes") - stat = uint32(item->Classes); + stat = int32(item->Classes); if (id == "races") - stat = uint32(item->Races); + stat = int32(item->Races); if (id == "maxcharges") - stat = uint32(item->MaxCharges); + stat = int32(item->MaxCharges); if (id == "itemtype") - stat = uint32(item->ItemType); + stat = int32(item->ItemType); if (id == "material") - stat = uint32(item->Material); + stat = int32(item->Material); if (id == "casttime") - stat = uint32(item->CastTime); + stat = int32(item->CastTime); if (id == "elitematerial") - stat = uint32(item->EliteMaterial); + stat = int32(item->EliteMaterial); if (id == "procrate") - stat = uint32(item->ProcRate); + stat = int32(item->ProcRate); if (id == "combateffects") - stat = uint32(item->CombatEffects); + stat = int32(item->CombatEffects); if (id == "shielding") - stat = uint32(item->Shielding); + stat = int32(item->Shielding); if (id == "stunresist") - stat = uint32(item->StunResist); + stat = int32(item->StunResist); if (id == "strikethrough") - stat = uint32(item->StrikeThrough); + stat = int32(item->StrikeThrough); if (id == "extradmgskill") - stat = uint32(item->ExtraDmgSkill); + stat = int32(item->ExtraDmgSkill); if (id == "extradmgamt") - stat = uint32(item->ExtraDmgAmt); + stat = int32(item->ExtraDmgAmt); if (id == "spellshield") - stat = uint32(item->SpellShield); + stat = int32(item->SpellShield); if (id == "avoidance") - stat = uint32(item->Avoidance); + stat = int32(item->Avoidance); if (id == "accuracy") - stat = uint32(item->Accuracy); + stat = int32(item->Accuracy); if (id == "charmfileid") - stat = uint32(item->CharmFileID); + stat = int32(item->CharmFileID); if (id == "factionmod1") - stat = uint32(item->FactionMod1); + stat = int32(item->FactionMod1); if (id == "factionmod2") - stat = uint32(item->FactionMod2); + stat = int32(item->FactionMod2); if (id == "factionmod3") - stat = uint32(item->FactionMod3); + stat = int32(item->FactionMod3); if (id == "factionmod4") - stat = uint32(item->FactionMod4); + stat = int32(item->FactionMod4); if (id == "factionamt1") - stat = uint32(item->FactionAmt1); + stat = int32(item->FactionAmt1); if (id == "factionamt2") - stat = uint32(item->FactionAmt2); + stat = int32(item->FactionAmt2); if (id == "factionamt3") - stat = uint32(item->FactionAmt3); + stat = int32(item->FactionAmt3); if (id == "factionamt4") - stat = uint32(item->FactionAmt4); + stat = int32(item->FactionAmt4); if (id == "augtype") - stat = uint32(item->AugType); + stat = int32(item->AugType); if (id == "ldontheme") - stat = uint32(item->LDoNTheme); + stat = int32(item->LDoNTheme); if (id == "ldonprice") - stat = uint32(item->LDoNPrice); + stat = int32(item->LDoNPrice); if (id == "ldonsold") - stat = uint32(item->LDoNSold); + stat = int32(item->LDoNSold); if (id == "banedmgraceamt") - stat = uint32(item->BaneDmgRaceAmt); + stat = int32(item->BaneDmgRaceAmt); if (id == "augrestrict") - stat = uint32(item->AugRestrict); + stat = int32(item->AugRestrict); if (id == "endur") - stat = uint32(item->Endur); + stat = int32(item->Endur); if (id == "dotshielding") - stat = uint32(item->DotShielding); + stat = int32(item->DotShielding); if (id == "attack") - stat = uint32(item->Attack); + stat = int32(item->Attack); if (id == "regen") - stat = uint32(item->Regen); + stat = int32(item->Regen); if (id == "manaregen") - stat = uint32(item->ManaRegen); + stat = int32(item->ManaRegen); if (id == "enduranceregen") - stat = uint32(item->EnduranceRegen); + stat = int32(item->EnduranceRegen); if (id == "haste") - stat = uint32(item->Haste); + stat = int32(item->Haste); if (id == "damageshield") - stat = uint32(item->DamageShield); + stat = int32(item->DamageShield); if (id == "recastdelay") - stat = uint32(item->RecastDelay); + stat = int32(item->RecastDelay); if (id == "recasttype") - stat = uint32(item->RecastType); + stat = int32(item->RecastType); if (id == "augdistiller") - stat = uint32(item->AugDistiller); + stat = int32(item->AugDistiller); if (id == "attuneable") - stat = uint32(item->Attuneable); + stat = int32(item->Attuneable); if (id == "nopet") - stat = uint32(item->NoPet); + stat = int32(item->NoPet); if (id == "potionbelt") - stat = uint32(item->PotionBelt); + stat = int32(item->PotionBelt); if (id == "stackable") - stat = uint32(item->Stackable); + stat = int32(item->Stackable); if (id == "notransfer") - stat = uint32(item->NoTransfer); + stat = int32(item->NoTransfer); if (id == "questitemflag") - stat = uint32(item->QuestItemFlag); + stat = int32(item->QuestItemFlag); if (id == "stacksize") - stat = uint32(item->StackSize); + stat = int32(item->StackSize); if (id == "potionbeltslots") - stat = uint32(item->PotionBeltSlots); + stat = int32(item->PotionBeltSlots); if (id == "book") - stat = uint32(item->Book); + stat = int32(item->Book); if (id == "booktype") - stat = uint32(item->BookType); + stat = int32(item->BookType); if (id == "svcorruption") - stat = uint32(item->SVCorruption); + stat = int32(item->SVCorruption); if (id == "purity") - stat = uint32(item->Purity); + stat = int32(item->Purity); if (id == "backstabdmg") - stat = uint32(item->BackstabDmg); + stat = int32(item->BackstabDmg); if (id == "dsmitigation") - stat = uint32(item->DSMitigation); + stat = int32(item->DSMitigation); if (id == "heroicstr") - stat = uint32(item->HeroicStr); + stat = int32(item->HeroicStr); if (id == "heroicint") - stat = uint32(item->HeroicInt); + stat = int32(item->HeroicInt); if (id == "heroicwis") - stat = uint32(item->HeroicWis); + stat = int32(item->HeroicWis); if (id == "heroicagi") - stat = uint32(item->HeroicAgi); + stat = int32(item->HeroicAgi); if (id == "heroicdex") - stat = uint32(item->HeroicDex); + stat = int32(item->HeroicDex); if (id == "heroicsta") - stat = uint32(item->HeroicSta); + stat = int32(item->HeroicSta); if (id == "heroiccha") - stat = uint32(item->HeroicCha); + stat = int32(item->HeroicCha); if (id == "heroicmr") - stat = uint32(item->HeroicMR); + stat = int32(item->HeroicMR); if (id == "heroicfr") - stat = uint32(item->HeroicFR); + stat = int32(item->HeroicFR); if (id == "heroiccr") - stat = uint32(item->HeroicCR); + stat = int32(item->HeroicCR); if (id == "heroicdr") - stat = uint32(item->HeroicDR); + stat = int32(item->HeroicDR); if (id == "heroicpr") - stat = uint32(item->HeroicPR); + stat = int32(item->HeroicPR); if (id == "heroicsvcorrup") - stat = uint32(item->HeroicSVCorrup); + stat = int32(item->HeroicSVCorrup); if (id == "healamt") - stat = uint32(item->HealAmt); + stat = int32(item->HealAmt); if (id == "spelldmg") - stat = uint32(item->SpellDmg); + stat = int32(item->SpellDmg); if (id == "ldonsellbackrate") - stat = uint32(item->LDoNSellBackRate); + stat = int32(item->LDoNSellBackRate); if (id == "scriptfileid") - stat = uint32(item->ScriptFileID); + stat = int32(item->ScriptFileID); if (id == "expendablearrow") - stat = uint32(item->ExpendableArrow); + stat = int32(item->ExpendableArrow); if (id == "clairvoyance") - stat = uint32(item->Clairvoyance); + stat = int32(item->Clairvoyance); // Begin Effects if (id == "clickeffect") - stat = uint32(item->Click.Effect); + stat = int32(item->Click.Effect); if (id == "clicktype") - stat = uint32(item->Click.Type); + stat = int32(item->Click.Type); if (id == "clicklevel") - stat = uint32(item->Click.Level); + stat = int32(item->Click.Level); if (id == "clicklevel2") - stat = uint32(item->Click.Level2); + stat = int32(item->Click.Level2); if (id == "proceffect") - stat = uint32(item->Proc.Effect); + stat = int32(item->Proc.Effect); if (id == "proctype") - stat = uint32(item->Proc.Type); + stat = int32(item->Proc.Type); if (id == "proclevel") - stat = uint32(item->Proc.Level); + stat = int32(item->Proc.Level); if (id == "proclevel2") - stat = uint32(item->Proc.Level2); + stat = int32(item->Proc.Level2); if (id == "worneffect") - stat = uint32(item->Worn.Effect); + stat = int32(item->Worn.Effect); if (id == "worntype") - stat = uint32(item->Worn.Type); + stat = int32(item->Worn.Type); if (id == "wornlevel") - stat = uint32(item->Worn.Level); + stat = int32(item->Worn.Level); if (id == "wornlevel2") - stat = uint32(item->Worn.Level2); + stat = int32(item->Worn.Level2); if (id == "focuseffect") - stat = uint32(item->Focus.Effect); + stat = int32(item->Focus.Effect); if (id == "focustype") - stat = uint32(item->Focus.Type); + stat = int32(item->Focus.Type); if (id == "focuslevel") - stat = uint32(item->Focus.Level); + stat = int32(item->Focus.Level); if (id == "focuslevel2") - stat = uint32(item->Focus.Level2); + stat = int32(item->Focus.Level2); if (id == "scrolleffect") - stat = uint32(item->Scroll.Effect); + stat = int32(item->Scroll.Effect); if (id == "scrolltype") - stat = uint32(item->Scroll.Type); + stat = int32(item->Scroll.Type); if (id == "scrolllevel") - stat = uint32(item->Scroll.Level); + stat = int32(item->Scroll.Level); if (id == "scrolllevel2") - stat = uint32(item->Scroll.Level2); + stat = int32(item->Scroll.Level2); safe_delete(inst); return stat; diff --git a/zone/mob.h b/zone/mob.h index 15f1728ec..fe0919f7e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -524,6 +524,7 @@ public: //More stuff to sort: + virtual bool IsRaidTarget() { return false; }; virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false); bool IsTargeted() const { return (targeted > 0); } inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;} @@ -552,7 +553,7 @@ public: void Shout(const char *format, ...); void Emote(const char *format, ...); void QuestJournalledSay(Client *QuestInitiator, const char *str); - uint32 GetItemStat(uint32 itemid, const char *identifier); + int32 GetItemStat(uint32 itemid, const char *identifier); int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false); uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); diff --git a/zone/npc.cpp b/zone/npc.cpp index 565a89598..f4e307bbc 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -358,6 +358,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float SetEmoteID(d->emoteid); InitializeBuffSlots(); CalcBonuses(); + raid_target = d->raid_target; } NPC::~NPC() diff --git a/zone/npc.h b/zone/npc.h index 6bc9813fe..5c8fd43bb 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -395,6 +395,8 @@ public: void mod_npc_killed_merit(Mob* c); void mod_npc_killed(Mob* oos); void AISpellsList(Client *c); + + bool IsRaidTarget() const { return raid_target; }; protected: @@ -500,6 +502,8 @@ protected: //mercenary stuff std::list mercTypeList; std::list mercDataList; + + bool raid_target; private: uint32 loottable_id; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 0bab3cfb0..816956d86 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2955,7 +2955,7 @@ XS(XS_Client_GetItemIDAt) Perl_croak(aTHX_ "Usage: Client::GetItemIDAt(THIS, slot_id)"); { Client * THIS; - uint32 RETVAL; + int32 RETVAL; dXSTARG; int16 slot_id = (int16)SvIV(ST(1)); @@ -2969,7 +2969,7 @@ XS(XS_Client_GetItemIDAt) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); RETVAL = THIS->GetItemIDAt(slot_id); - XSprePUSH; PUSHu((UV)RETVAL); + XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); } @@ -2982,7 +2982,7 @@ XS(XS_Client_GetAugmentIDAt) Perl_croak(aTHX_ "Usage: Client::GetAugmentIDAt(THIS, slot_id, augslot)"); { Client * THIS; - uint32 RETVAL; + int32 RETVAL; dXSTARG; int16 slot_id = (int16)SvIV(ST(1)); int16 augslot = (uint8)SvIV(ST(2)); @@ -2997,7 +2997,7 @@ XS(XS_Client_GetAugmentIDAt) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); RETVAL = THIS->GetAugmentIDAt(slot_id, augslot); - XSprePUSH; PUSHu((UV)RETVAL); + XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 86cf58981..14d84a70e 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -7298,7 +7298,7 @@ XS(XS_Mob_GetItemStat) Perl_croak(aTHX_ "Usage: Mob::GetItemStat(THIS, itemid, stat)"); { Mob * THIS; - uint32 RETVAL; + int32 RETVAL; uint32 itemid = (uint32)SvUV(ST(1)); Const_char * stat = (Const_char *)SvPV_nolen(ST(2)); dXSTARG; @@ -7313,7 +7313,7 @@ XS(XS_Mob_GetItemStat) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); RETVAL = THIS->GetItemStat(itemid, stat); - XSprePUSH; PUSHu((UV)RETVAL); + XSprePUSH; PUSHi((IV)RETVAL); } XSRETURN(1); } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index f748ddc68..bd6708a88 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -2015,7 +2015,7 @@ float Mob::GetSpecialProcChances(uint16 hand) ProcBonus += static_cast(mydex/35) + static_cast(itembonuses.HeroicDEX / 25); ProcChance += ProcChance * ProcBonus / 100.0f; } else { - /*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.” + /*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.” Kayen: Most reports suggest a ~ 6% chance to Headshot which consistent with above.*/ ProcChance = (static_cast(mydex/35) + static_cast(itembonuses.HeroicDEX / 25))/100.0f; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b14e63f26..0d1653453 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1115,7 +1115,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { "npc_types.emoteid," "npc_types.spellscale," "npc_types.healscale," - "npc_types.no_target_hotkey"; + "npc_types.no_target_hotkey," + "npc_types.raid_target"; MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); @@ -1302,6 +1303,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->spellscale = atoi(row[r++]); tmpNPCType->healscale = atoi(row[r++]); tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false; + tmpNPCType->raid_target = atoi(row[r++]) == 0 ? false : true; // If NPC with duplicate NPC id already in table, // free item we attempted to add. diff --git a/zone/zonedump.h b/zone/zonedump.h index 3b5b91577..98841630a 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -125,6 +125,7 @@ struct NPCType float spellscale; float healscale; bool no_target_hotkey; + bool raid_target; }; /*