diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..5bdd64324 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: cpp +compiler: gcc +before_install: + - sudo apt-get update -qq + - sudo apt-get install -y libmysqlclient-dev libperl-dev libboost-dev liblua5.1-0-dev zlib1g-dev +script: + - cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON + - make + - ./bin/tests +branches: + only: master +notifications: + email: false + irc: + channels: "irc.eqemulator.net#eqemucoders" +os: linux \ No newline at end of file diff --git a/README.md b/README.md index edee03cca..0c973ed43 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,53 @@ -EQEmu - Custom Game Implementation for EverQuest +EQEmu +=== -Dependencies can be obtained at http://eqemu.github.io +[![Build Status](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) + +Overview +--- + +EQEmu is a custom server implementation for EverQuest + +Dependencies +--- + +For Windows: http://eqemu.github.io + +Login Server dependencies for Windows/Linux/OSX: http://eqemu.github.io + +For Debian based distros (adjust to your local flavor): + +- libmysqlclient-dev +- libperl-dev +- liblua5.1-0-dev (5.2 should work as well) +- libboost-dev + +Further instructions on building the source can be found on the +[wiki](http://wiki.eqemulator.org/i?M=Wiki). + +Bug reports +--- + +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 +bugs/get help with bugs. + +Contributions +--- + +The preferred way to contribute is to fork the repo and submit a pull request on +GitHub. If you need help with your changes, you can always post on the forums or +try IRC. You can also post unified diffs (`git diff` should do the trick) on the +[Server Code Submissions](http://www.eqemulator.org/forums/forumdisplay.php?f=669) +forum, although pull requests will be much quicker and easier on all parties. + +Contact +--- + - **User IRC Channel**: `#eqemu` on `irc.eqemulator.net` + - **Developer IRC Channel**: `#eqemucoders` on `irc.eqemulator.net` + +- [EQEmulator Forums](http://www.eqemulator.org/forums) +- [EQEmulator Wiki](http://wiki.eqemulator.org/i?M=Wiki) -More Information: https://github.com/EQEmu/Server/wiki diff --git a/changelog.txt b/changelog.txt index 464669fd5..556069f21 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,32 @@ 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) +- InCombat, OutofCombat - Used together to restrict spells to only be cast while +in/out of combat (beneficial) or if target is in/out of combat (detrimental). +-min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster. +*This will require further work to fully implement but will work with 90% of live spells as is. +*If making custom spells do not include effects that can't be scaled (like a spell trigger) +- min_rage sets minimum distance range that must be away from target. + +Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql + == 07/31/2014 == Uleat: More inventory slot constant conversions. This should be the bulk of everything..but, due to the size of the server code, there may be some hidden ones. (client_packet.cpp and the client translators still need a thorough review.) diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index 8aa29bea6..d89976b88 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -70,7 +70,7 @@ void ExportSpells(SharedDatabase *db) { } char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM spells_new ORDER BY id"; + const char *query = "SELECT * FROM spells_new ORDER BY id"; MYSQL_RES *result; MYSQL_ROW row; if(db->RunQuery(query, strlen(query), errbuf, &result)) { @@ -176,7 +176,7 @@ void ExportBaseData(SharedDatabase *db) { } char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM base_data ORDER BY level, class"; + const char *query = "SELECT * FROM base_data ORDER BY level, class"; MYSQL_RES *result; MYSQL_ROW row; if(db->RunQuery(query, strlen(query), errbuf, &result)) { diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 8115600bf..0bbbaa3fd 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -61,7 +61,7 @@ int main(int argc, char **argv) { int GetSpellColumns(SharedDatabase *db) { char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "DESCRIBE spells_new"; + const char *query = "DESCRIBE spells_new"; MYSQL_RES *result; MYSQL_ROW row; int res = 0; @@ -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); @@ -234,4 +234,4 @@ void ImportBaseData(SharedDatabase *db) { } fclose(f); -} \ No newline at end of file +} 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/common/shareddb.cpp b/common/shareddb.cpp index 7dd4a6fa7..7a9858789 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); - sp[tempid].uninterruptable=atoi(row[146]); + sp[tempid].uninterruptable=atoi(row[146]) != 0; sp[tempid].ResistDiff=atoi(row[147]); sp[tempid].dot_stacking_exempt=atoi(row[148]); sp[tempid].RecourseLink = atoi(row[150]); @@ -1720,11 +1720,13 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].descnum = atoi(row[155]); sp[tempid].effectdescnum = atoi(row[157]); + sp[tempid].npc_no_los = atoi(row[159]) != 0; sp[tempid].reflectable = atoi(row[161]) != 0; sp[tempid].bonushate=atoi(row[162]); sp[tempid].EndurCost=atoi(row[166]); sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; sp[tempid].HateAdded=atoi(row[173]); sp[tempid].EndurUpkeep=atoi(row[174]); sp[tempid].numhitstype = atoi(row[175]); @@ -1740,17 +1742,26 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { sp[tempid].viral_targets = atoi(row[191]); sp[tempid].viral_timer = atoi(row[192]); sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = (float)atoi(row[194]); - sp[tempid].directional_end = (float)atoi(row[195]); + sp[tempid].directional_start = static_cast(atoi(row[194])); + sp[tempid].directional_end = static_cast(atoi(row[195])); sp[tempid].not_extendable = atoi(row[197]) != 0; sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].rank = atoi(row[208]); sp[tempid].powerful_flag=atoi(row[209]); sp[tempid].CastRestriction = atoi(row[211]); sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].NotOutofCombat = atoi(row[213]) != 0; - sp[tempid].NotInCombat = atoi(row[214]) != 0; + sp[tempid].InCombat = atoi(row[213]) != 0; + sp[tempid].OutofCombat = atoi(row[214]) != 0; + sp[tempid].aemaxtargets = atoi(row[218]); + sp[tempid].maxtargets = atoi(row[219]); sp[tempid].persistdeath = atoi(row[224]) != 0; + sp[tempid].min_dist = atof(row[227]); + sp[tempid].min_dist_mod = atof(row[228]); + sp[tempid].max_dist = atof(row[229]); + sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].min_range = static_cast(atoi(row[231])); sp[tempid].DamageShieldType = 0; } mysql_free_result(result); @@ -1764,7 +1775,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { int SharedDatabase::GetMaxBaseDataLevel() { char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT MAX(level) FROM base_data"; + const char *query = "SELECT MAX(level) FROM base_data"; MYSQL_RES *result; MYSQL_ROW row; int32 ret = 0; @@ -1815,7 +1826,7 @@ bool SharedDatabase::LoadBaseData() { void SharedDatabase::LoadBaseData(void *data, int max_level) { char *base_ptr = reinterpret_cast(data); char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; + const char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; MYSQL_RES *result; MYSQL_ROW row; diff --git a/common/spdat.cpp b/common/spdat.cpp index 75f1e9b88..8e93f4e1f 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1040,6 +1040,15 @@ bool IsCastonFadeDurationSpell(uint16 spell_id) return false; } +bool IsPowerDistModSpell(uint16 spell_id) +{ + if (IsValidSpell(spell_id) && + (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) + return true; + + return false; +} + uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) diff --git a/common/spdat.h b/common/spdat.h index 69082d305..40f5e658f 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -131,10 +131,10 @@ typedef enum { /* 41 */ ST_Group = 0x29, /* 42 */ ST_Directional = 0x2a, //ae around this target between two angles /* 43 */ ST_GroupClientAndPet = 0x2b, -/* 44 */ ST_Beam = 0x2c, //like directional but facing in front of you always +/* 44 */ //ST_Beam = 0x2c, //like directional but facing in front of you always /* 45 */ //ST_Ring = 0x2d, // Like a mix of PB ae + rain spell(has ae duration) /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target -/* 47 */ //ST_PetMaster = 0x2e, // uses the master as target +/* 47 */ ST_PetMaster = 0x2f, // uses the master as target } SpellTargetType; typedef enum { @@ -690,15 +690,15 @@ struct SPDat_Spell_Struct /* 156 */ //int typedescnum; // eqstr of type description /* 157 */ int effectdescnum; // eqstr of effect description /* 158 */ //Category Desc ID 3 -/* 159 */ //bool npc_no_los; +/* 159 */ bool npc_no_los; /* 161 */ bool reflectable; /* 162 */ int bonushate; /* 163 */ /* 164 */ // for most spells this appears to mimic ResistDiff /* 166 */ int EndurCost; /* 167 */ int8 EndurTimerIndex; -/* 168 */ //int IsDisciplineBuff; //Will goto the combat window when cast -/* 169 */ +/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast +/* 169 - 172*/ //These are zero for ALL spells /* 173 */ int HateAdded; /* 174 */ int EndurUpkeep; /* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. @@ -721,22 +721,30 @@ struct SPDat_Spell_Struct /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones -/* 201 - 202 */ +/* 201 */ int viral_range; +/* 202 */ /* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented) /* 204 - 206 */ /* 207 */ int spellgroup; -/* 208 */ // int rank - increments AA effects with same name +/* 208 */ int rank; //increments AA effects with same name /* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though /* 210 */ // bool DurationFrozen; ??? /* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat /* 212 */ bool AllowRest; -/* 213 */ bool NotOutofCombat; //Fail if cast out of combat -/* 214 */ bool NotInCombat; //Fail if cast in combat -/* 215 - 218 */ -/* 219 */ //int maxtargets; // is used for beam and ring spells for target # limits (not implemented) -/* 220 - 223 */ +/* 213 */ bool InCombat; //Allow spell if target is in combat +/* 214 */ bool OutofCombat; //Allow spell if target is out of combat +/* 215 - 217 */ +/* 218 */ int aemaxtargets; //Is used for various AE effects +/* 219 */ int maxtargets; //Is used for beam and ring spells for target # limits (not implemented) +/* 220 - 223 */ /* 224 */ bool persistdeath; // buff doesn't get stripped on death -/* 225 - 236 */ // Not in DB +/* 225 - 226 */ +/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) +/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) +/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) +/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) +/* 231 */ float min_range; //Min casting range +/* 232 - 236 */ uint8 DamageShieldType; // This field does not exist in spells_us.txt }; @@ -837,6 +845,7 @@ bool IsPersistDeathSpell(uint16 spell_id); bool IsSuspendableSpell(uint16 spell_id); uint32 GetMorphTrigger(uint32 spell_id); bool IsCastonFadeDurationSpell(uint16 spell_id); +bool IsPowerDistModSpell(uint16 spell_id); uint32 GetPartialMeleeRuneReduction(uint32 spell_id); uint32 GetPartialMagicRuneReduction(uint32 spell_id); uint32 GetPartialMeleeRuneAmount(uint32 spell_id); diff --git a/utils/scripts/.gitignore b/utils/scripts/.gitignore new file mode 100644 index 000000000..eba9f59cb --- /dev/null +++ b/utils/scripts/.gitignore @@ -0,0 +1 @@ +opcode_handlers_output \ No newline at end of file diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py new file mode 100644 index 000000000..6cad4ade0 --- /dev/null +++ b/utils/scripts/opcode_handlers.py @@ -0,0 +1,1471 @@ +#! /usr/bin/env python +# + +""" +'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 +import os + +from time import time, ctime + + +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 = {} # x[key='Client'][key='OP_CodeName'](value='0x####') +server_opcodes = {} # x[key='OP_CodeName'](value=) - opcodes apply to all servers + +client_encodes = {} # x[key='Client'](value='OP_CodeName') +client_decodes = {} # x[key='Client'](value='OP_CodeName') + +server_handlers = {} # x[key='Server'][key='OP_CodeName'](value='[%X] Near Handler::ReferenceFunction') + +out_files = {} # x[key='Server'](value=) + +#statistics = {} +#report_entries = {} + + +def main(): + """ Call TASK METHODS independently and track success """ + + fault = False + faults = [] + + print('Initializing...') + + if not fault: + fault = not createoutputdirectory() + + if fault: + faults.append('createoutputdirectory()') + + if not fault: + fault = not opendebugfile() + + if fault: + faults.append('opendebugfile()') + + if not fault: + fault = not openundefinedfile() + + if fault: + faults.append('openundefinedfile()') + + if not fault: + print('Loading source data...') + + if not fault: + fault = not loadclientopcodes() + + if fault: + faults.append('loadclientopcodes()') + + if not fault: + fault = not loadserveropcodes() + + if fault: + faults.append('loadserveropcodes()') + + if not fault: + fault = not loadclienttranslators() + + if fault: + faults.append('loadclienttranslators()') + + if not fault: + fault = not loadserverhandlers() + + if fault: + faults.append('loadserverhandlers()') + + if not fault: + fault = not discoverserverhandlers() + + if fault: + faults.append('discoverserverhandlers()') + + if not fault: + fault = not clearemptyserverentries() + + if fault: + faults.append('clearemptyserverentries()') + + if not fault: + print('Creating output streams...') + + if not fault: + fault = not openoutputfiles() + + if fault: + faults.append('openoutputfiles()') + + if not fault: + print('Parsing opcode data...') + + if not fault: + fault = not parseclientopcodedata() + + if fault: + faults.append('parseclientopcodedata()') + + if not fault: + fault = not parseserveropcodedata() + + if fault: + faults.append('parseserveropcodedata()') + + if not fault: + print('Destroying output streams...') + + if not closeoutputfiles(): + fault = True + faults.append('closeoutputfiles()') + + if not closedebugfile(): + fault = True + faults.append('closedebugfile()') + + if fault and len(faults) > 0: + message = 'Script failed due to errors in:\n' + + for entry in faults: + message += ' {0}\n'.format(entry) + + print(message) + + return + + +def createoutputdirectory(): + """ Check for OUTPUT DIRECTORY - create if does not exist """ + + try: + output_path = '{0}/utils/scripts/opcode_handlers_output'.format(base_path) + + vprint(output_path) + + if not os.path.exists(output_path): + os.mkdir(output_path) + + return True + except: + print('(Exception Error: {0}) createoutputdirectory()'.format(sys.exc_info()[0])) + + return False + + +def opendebugfile(): + """ DEBUG FILE should always open """ + + try: + file_name = '{0}/utils/scripts/opcode_handlers_output/DEBUG.txt'.format(base_path) + + vprint(file_name) + + 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 = '/patch_{0}.conf'.format(client) + + file_name = '{0}/utils/patches{1}'.format( + base_path, + short_name + ) + + vprint(file_name) + + with open(file_name, 'r') as data_file: + 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) + + if not key_begin == 0 or key_end < 0: + continue + + val_begin = data_line.find('0x', key_end) + val_end = val_begin + 6 + + if val_begin < 0: + continue + + value = int(data_line[(val_begin + 2):val_end].lower(), 16) + + if value == 0: + continue + + client_opcodes[client][data_line[key_begin:key_end]] = '0x{0}'.format(hex(value)[2:].zfill(4)) + + 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: + 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: + vprint('Deleting \'{0}\' client from search criteria...'.format(bad_client)) + + client_list.remove(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: + server_opcodes['OP_Unknown'] = 0 + value = 1 + + dprint('(manual) \'Servers\' [OP_Unknown] = {0}\n'.format(server_opcodes['OP_Unknown'])) + + file_name = '{0}/common/emu_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) + + if val_begin < 0 or val_end < 0: + continue + + if data_line[:1] == 'N': + server_opcodes[data_line[val_begin:val_end]] = value + value += 1 + + 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() + + dprint('->close: \'{0}\'\n'.format(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) + + if val_begin < 0 or val_end < 0: + continue + + if data_line[:1] == 'N': + server_opcodes[data_line[val_begin:val_end]] = value + value += 1 + + 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: + 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: + try: + 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, + short_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) + + if val_begin < 0 or val_end < 0: + continue + + if data_line[:1] == 'E': + client_encodes[client].append(data_line[val_begin:val_end]) + + 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]) + + 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: + print('(Exception Error: {0}) loadclienttranslators() [{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 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)) + + 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 """ + + # TODO: handle remarked out definitions in file (i.e., // and /**/) + + dprint('entering \'loadserverhandlers()\'\n') + + bad_servers = [] + + for server in server_list: + try: + if server == 'Login': + vprint('No pre-designated server opcode handlers for \'Login\'') + dprint('->pass: \'Login\' server\n') + + continue + elif server == 'World': + vprint('No pre-designated server opcode handlers for \'World\'') + dprint('->pass: \'World\' server\n') + + continue + elif server == 'Zone': + file_name = '{0}/zone/client_packet.cpp'.format(base_path) + + vprint(file_name) + + with open(file_name, 'r') as data_file: + dprint('->open: \'{0}\' in \'r\' mode\n'.format(file_name)) + + server_handlers[server] = {} + 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 step_1 is False: + if data_line[:19] == 'void MapOpcodes() {': + step_1 = True + + continue + + 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) + + 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 + + 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 + + if data_line[:1].isalpha(): + hint_begin = 0 + hint_end = data_line.find('(') + + if not hint_end < 0: + hint_begin = hint_end - 1 + + 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, + hint_begin, + server, + 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': + vprint('No pre-designated server opcode handlers for \'UCS\'') + dprint('->pass: \'UCS\' server\n') + + continue + else: + vprint('No pre-designated server opcode handlers for \'{0}\''.format(server)) + dprint('->pass: \'{0}\' server\n'.format(server)) + + continue + except: + 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 bad_server in server_handlers: + vprint('Deleting stale entries for \'{0}\' server...'.format(bad_server)) + + del server_handlers[bad_server] + + 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 + + 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: + if not server in locations: + locations[server] = [] + + if 'Login' in locations: + locations['Login'].append('/loginserver/Client.cpp') + locations['Login'].append('/loginserver/ServerManager.cpp') + locations['Login'].append('/loginserver/WorldServer.cpp') + + if 'World' in locations: + locations['World'].append('/world/client.cpp') + + if 'Zone' in locations: + locations['Zone'].append('/zone/AA.cpp') + locations['Zone'].append('/zone/attack.cpp') + locations['Zone'].append('/zone/bot.cpp') + locations['Zone'].append('/zone/client.cpp') + locations['Zone'].append('/zone/client_packet.cpp') + locations['Zone'].append('/zone/client_process.cpp') + locations['Zone'].append('/zone/command.cpp') + locations['Zone'].append('/zone/corpse.cpp') + locations['Zone'].append('/zone/doors.cpp') + locations['Zone'].append('/zone/effects.cpp') + locations['Zone'].append('/zone/entity.cpp') + locations['Zone'].append('/zone/exp.cpp') + locations['Zone'].append('/zone/groups.cpp') + locations['Zone'].append('/zone/guild.cpp') + locations['Zone'].append('/zone/guild_mgr.cpp') + locations['Zone'].append('/zone/horse.cpp') + locations['Zone'].append('/zone/inventory.cpp') + locations['Zone'].append('/zone/loottables.cpp') + locations['Zone'].append('/zone/merc.cpp') + locations['Zone'].append('/zone/mob.cpp') + locations['Zone'].append('/zone/MobAI.cpp') + locations['Zone'].append('/zone/Object.cpp') + locations['Zone'].append('/zone/pathing.cpp') + locations['Zone'].append('/zone/petitions.cpp') + locations['Zone'].append('/zone/questmgr.cpp') + locations['Zone'].append('/zone/raids.cpp') + locations['Zone'].append('/zone/special_attacks.cpp') + locations['Zone'].append('/zone/spells.cpp') + locations['Zone'].append('/zone/spell_effects.cpp') + locations['Zone'].append('/zone/tasks.cpp') + locations['Zone'].append('/zone/titles.cpp') + locations['Zone'].append('/zone/tradeskills.cpp') + locations['Zone'].append('/zone/trading.cpp') + locations['Zone'].append('/zone/trap.cpp') + locations['Zone'].append('/zone/tribute.cpp') + locations['Zone'].append('/zone/worldserver.cpp') + locations['Zone'].append('/zone/zone.cpp') + locations['Zone'].append('/zone/zonedb.cpp') + locations['Zone'].append('/zone/zoning.cpp') + + if 'UCS' in locations: + locations['UCS'].append('/ucs/clientlist.cpp') + locations['UCS'].append('/ucs/database.cpp') + + for server in server_list: + if not server in server_handlers: + server_handlers[server] = {} + + for location in locations[server]: + try: + file_name = '{0}{1}'.format( + base_path, + location) + + 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('(') + + if not hint_end < 0: + hint_begin = hint_end - 1 + + 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 = 'Near {0}'.format(data_line[hint_begin:hint_end]) + + break + + hint_begin -= 1 + + 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 = 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 = 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 = 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: + dprint('\nILLEGAL OPCODE FOUND: ..{0}({1}:{2}) \'{3}\'\n'.format( + location, + line_no, + key_begin, + data_line[key_begin:key_end] + )) + + continue + + if not data_line[key_begin:key_end] in server_handlers[server]: + 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, + '{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: + 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 not server in server_handlers or not len(server_handlers[server]) > 0: + bad_servers.append(server) + else: + bad_opcodes = [] + + for opcode in server_handlers[server]: + if len(server_handlers[server][opcode]) == 0: + bad_opcodes.append(opcodes) + + for bad_opcode in bad_opcodes: + del server_handlers[server][bad_opcode] + + if len(server_handlers[server]) == 0: + bad_servers.append(server) + + for bad_server in bad_servers: + vprint('Deleting \'{0}\' server from search criteria...'.format(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 """ + + dprint('entering \'openoutputfiles()\'\n') + + try: + file_name = '{0}/utils/scripts/opcode_handlers_output/REPORT.txt'.format(base_path) + + vprint(file_name) + + 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}/utils/scripts/opcode_handlers_output/{1}_opcode_handlers.txt'.format( + base_path, + client + ) + + vprint(file_name) + + out_files[client] = open(file_name, 'w') + + 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()) + ) + ) + + for server in server_list: + file_name = '{0}/utils/scripts/opcode_handlers_output/{1}_opcode_handlers.txt'.format( + base_path, + server + ) + + vprint(file_name) + + out_files[server] = open(file_name, 'w') + + 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()) + ) + ) + + dprint('leaving \'openoutputfiles()\'\n\n') + + return True + except: + 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] + + 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] + + dprint('leaving \'openoutputfiles(): EXCEPTION ERROR\'\n\n') + + return False + + +def parseclientopcodedata(): + """ Process CLIENT OPCODE cross-link references """ + + dprint('entering \'parseclientopcodedata()\'\n') + + for client in client_list: + server_max_len = 0 + + for server in server_list: + if len(server) > server_max_len: + server_max_len = len(server) + + client_keys = client_opcodes[client].keys() + client_keys.sort() + + for client_opcode in client_keys: + handled = client_opcode in server_opcodes + + if handled is True: + encoded = client_opcode in client_encodes[client] + decoded = client_opcode in client_decodes[client] + else: + encoded = 'N/A' + decoded = 'N/A' + + message = 'Opcode: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( + client_opcode, + client_opcodes[client][client_opcode], + handled, + encoded, + decoded + ) + + for server in server_list: + if client_opcode in server_handlers[server] and len(server_handlers[server][client_opcode]) > 0: + handler_list = server_handlers[server][client_opcode] + handler_list.sort() + + for handler_entry in handler_list: + 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 + ) + else: + message += ' Server: {0} (0000) | Handler: N/A\n'.format( + server.ljust(len(server) + (server_max_len - len(server)), ' ') + ) + + message += '\n' + + cprint(client, message) + + dprint('->parse: \'{0}\' client\n'.format(client)) + + dprint('leaving \'parseclientopcodedata()\'\n\n') + + return True + + +def parseserveropcodedata(): + """ Process SERVER OPCODE cross-link references """ + + dprint('entering \'parseserveropcodedata()\'\n') + + for server in server_list: + client_max_len = 0 + + for client in client_list: + if len(client) > client_max_len: + client_max_len = len(client) + + handler_keys = server_handlers[server].keys() + handler_keys.sort() + + for handler_opcode in handler_keys: + handler_list = server_handlers[server][handler_opcode] + handler_list.sort() + + message = '' + + for handler_entry in handler_list: + message += 'Opcode: {0} ({1}) | Handler: {2}\n'.format( + handler_opcode, + '{0}'.format(server_opcodes[handler_opcode]).zfill(4), + handler_entry) + + for client in client_list: + if handler_opcode in client_opcodes[client]: + val1 = client_opcodes[client][handler_opcode] + val2 = 'True' + val3 = '{0}'.format(handler_opcode in client_encodes[client]) + val4 = '{0}'.format(handler_opcode in client_decodes[client]) + else: + val1 = '0x0000' + val2 = 'False' + val3 = 'N/A' + val4 = 'N/A' + + message += ' Client: {0} ({1}) | Handled: {2} | Encoded: {3} | Decoded: {4}\n'.format( + client.ljust(len(client) + (client_max_len - len(client)), ' '), + 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)), ' ') + ) + + message += '\n' + + sprint(server, message) + + dprint('->parse: \'{0}\' server\n'.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] + + 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] + + dprint('->close: \'{0}\'\n'.format(file_name)) + + dprint('leaving \'closeoutputfiles()\'\n\n') + + return True + + +def closedebugfile(): + """ Close DEBUG FILE - last performed action to catch late messages """ + + 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_02_spells_new.sql b/utils/sql/git/required/2014_08_02_spells_new.sql new file mode 100644 index 000000000..f4e74158a --- /dev/null +++ b/utils/sql/git/required/2014_08_02_spells_new.sql @@ -0,0 +1,18 @@ +-- spells new talbe update +ALTER TABLE `spells_new` CHANGE `NotOutofCombat` `InCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `NotInCombat` `OutofCombat` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field201` `viral_range` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field218` `aemaxtargets` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` ADD `field225` int( 11 ) NOT NULL DEFAULT '0' AFTER `persistdeath`; +ALTER TABLE `spells_new` ADD `field226` int( 11 ) NOT NULL DEFAULT '0' AFTER `field225`; +ALTER TABLE `spells_new` ADD `min_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `field226`; +ALTER TABLE `spells_new` ADD `min_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist`; +ALTER TABLE `spells_new` ADD `max_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist_mod`; +ALTER TABLE `spells_new` ADD `max_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `max_dist`; +ALTER TABLE `spells_new` ADD `min_range` int( 11 ) NOT NULL DEFAULT '0' AFTER `max_dist_mod`; +ALTER TABLE `spells_new` ADD `field232` int( 11 ) NOT NULL DEFAULT '0' AFTER `min_range`; +ALTER TABLE `spells_new` ADD `field233` int( 11 ) NOT NULL DEFAULT '0' AFTER `field232`; +ALTER TABLE `spells_new` ADD `field234` int( 11 ) NOT NULL DEFAULT '0' AFTER `field233`; +ALTER TABLE `spells_new` ADD `field235` int( 11 ) NOT NULL DEFAULT '0' AFTER `field234`; +ALTER TABLE `spells_new` ADD `field236` int( 11 ) NOT NULL DEFAULT '0' AFTER `field235`; + 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/StringIDs.h b/zone/StringIDs.h index 9a222d276..b1e13edca 100644 --- a/zone/StringIDs.h +++ b/zone/StringIDs.h @@ -249,6 +249,7 @@ #define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. #define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. +#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. @@ -316,6 +317,10 @@ #define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. #define PET_NOT_CASTING 9264 //Not casting spells, Master. #define PET_CASTING 9291 //Casting spells normally, Master. +#define NO_CAST_IN_COMBAT 9190 //You can not cast this spell while in combat. +#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat. +#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat. +#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat. #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. diff --git a/zone/attack.cpp b/zone/attack.cpp index 2403293e6..3195b4234 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -311,7 +311,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database if(skillinuse == SkillArchery) - hitBonus -= hitBonus*(RuleR(Combat, ArcheryHitPenalty)*100.0f); + hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty); //Calculate final chance to hit chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f); 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 01e768c1f..ff8b9e717 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1231,7 +1231,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 @@ -1257,12 +1256,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; @@ -9140,7 +9142,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/effects.cpp b/zone/effects.cpp index c298974db..1b1eeb2ea 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -740,6 +740,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ float dist = caster->GetAOERange(spell_id); float dist2 = dist * dist; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + float dist_targ = 0; bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); @@ -755,7 +757,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (curmob == caster && !affect_caster) //watch for caster too continue; - if (center->DistNoRoot(*curmob) > dist2) //make sure they are in range + + dist_targ = center->DistNoRoot(*curmob); + if (dist_targ > dist2) //make sure they are in range + continue; + if (dist_targ < min_range2) //make sure they are in range continue; if (isnpc && curmob->IsNPC()) { //check npc->npc casting FACTION_VALUE f = curmob->GetReverseFactionCon(caster); @@ -786,6 +792,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; } + curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ); + //if we get here... cast the spell. if (IsTargetableAESpell(spell_id) && bad) { if (iCounter < MAX_TARGETS_ALLOWED) { diff --git a/zone/entity.cpp b/zone/entity.cpp index 3d316585d..5ff19847f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -4538,7 +4538,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType) return ClosestMob; } -void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list) +void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list) { auto it = mob_list.begin(); while (it != mob_list.end()) { @@ -4547,15 +4547,15 @@ void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, ++it; continue; } - int32 x_diff = ptr->GetX() - start->GetX(); - int32 y_diff = ptr->GetY() - start->GetY(); - int32 z_diff = ptr->GetZ() - start->GetZ(); + float x_diff = ptr->GetX() - start->GetX(); + float y_diff = ptr->GetY() - start->GetY(); + float z_diff = ptr->GetZ() - start->GetZ(); x_diff *= x_diff; y_diff *= y_diff; z_diff *= z_diff; - if ((x_diff + y_diff) <= (radius * radius)) + if ((x_diff + y_diff) <= (radius * radius) && (x_diff + y_diff) >= (min_radius * min_radius)) if(z_diff <= (height * height)) m_list.push_back(ptr); diff --git a/zone/entity.h b/zone/entity.h index 8b94b0e0a..c1ba0936c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -406,7 +406,7 @@ public: void GetObjectList(std::list &o_list); void GetDoorsList(std::list &d_list); void GetSpawnList(std::list &d_list); - void GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list &m_list); + void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list &m_list); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); diff --git a/zone/groups.cpp b/zone/groups.cpp index 6aee5f38b..d948eb476 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -658,6 +658,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { range = caster->GetAOERange(spell_id); float range2 = range*range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; // caster->SpellOnTarget(spell_id, caster); @@ -673,7 +674,8 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { else if(members[z] != nullptr) { distance = caster->DistNoRoot(*members[z]); - if(distance <= range2) { + if(distance <= range2 && distance >= min_range2) { + members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index bb39d224a..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(); + } } } @@ -567,20 +570,25 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) //So keep a list of entity ids and look up after std::list id_list; range = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; + float dist_targ = 0; auto iterator = list.begin(); while (iterator != list.end()) { tHateEntry *h = (*iterator); if(range > 0) { - if(caster->DistNoRoot(*h->ent) <= range) + dist_targ = caster->DistNoRoot(*h->ent); + if(dist_targ <= range && dist_targ >= min_range2) { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ); } } else { id_list.push_back(h->ent->GetID()); + h->ent->CalcSpellPowerDistanceMod(spell_id, 0, caster); } ++iterator; } 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 a55390050..a6baabd1a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -182,6 +182,7 @@ Mob::Mob(const char* in_name, has_numhits = false; has_MGB = false; has_ProjectIllusion = false; + SpellPowerDistanceMod = 0; if(in_aa_title>0) aa_title = in_aa_title; @@ -3536,7 +3537,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) @@ -3549,7 +3550,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) @@ -3558,316 +3559,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 1793e0864..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); @@ -615,6 +616,9 @@ public: bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); int16 GetSlowMitigation() const {return slow_mitigation;} + void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); + inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; + inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); @@ -1114,6 +1118,7 @@ protected: bool has_numhits; bool has_MGB; bool has_ProjectIllusion; + int16 SpellPowerDistanceMod; // Bind wound Timer bindwound_timer; 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/spell_effects.cpp b/zone/spell_effects.cpp index 0ee88a154..8a45cf391 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -186,6 +186,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].numhits = numhit; } + if (!IsPowerDistModSpell(spell_id)) + SetSpellPowerDistanceMod(0); + // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) { @@ -198,6 +201,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) effect_value = GetMaxHP(); + if (GetSpellPowerDistanceMod()) + effect_value = effect_value*(GetSpellPowerDistanceMod()/100); + #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; #endif @@ -2705,7 +2711,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (buffslot >= 0) break; - if(IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + if(!spells[spell_id].uninterruptable && IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i]) InterruptSpell(); break; @@ -6444,4 +6450,24 @@ bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){ return false; } - \ No newline at end of file + +void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) +{ + if (IsPowerDistModSpell(spell_id)){ + + float distance = 0; + + if (caster && !range) + distance = caster->CalculateDistance(GetX(), GetY(), GetZ()); + else + distance = sqrt(range); + + float dm_range = spells[spell_id].max_dist - spells[spell_id].min_dist; + float dm_mod_interval = spells[spell_id].max_dist_mod - spells[spell_id].min_dist_mod; + float dist_from_min = distance - spells[spell_id].min_dist; + float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range)); + mod *= 100.0f; + + SetSpellPowerDistanceMod(static_cast(mod)); + } +} \ No newline at end of file diff --git a/zone/spells.cpp b/zone/spells.cpp index fac7e2c59..5d891629e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1034,7 +1034,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, mlog(SPELLS__CASTING, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(SkillChanneling)); - if(MakeRandomFloat(0, 100) > channelchance) { + if(!spells[spell_id].uninterruptable && MakeRandomFloat(0, 100) > channelchance) { mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: interrupted.", spell_id); InterruptSpell(); return; @@ -1384,6 +1384,52 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce return false; } + //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) + if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && spell_target->IsEngaged()) || + (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && IsEngaged()) || + (IsClient() && CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_IN_COMBAT); + else + Message_StringID(13,NO_CAST_IN_COMBAT); + + return false; + } + } + } + + //Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) + else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){ + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) || + (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){ + Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string + return false; + } + } + + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && !IsEngaged()) || + (IsClient() && !CastToClient()->GetAggroCount())){ + if (IsDiscipline(spell_id)) + Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT); + else + Message_StringID(13,NO_CAST_OUT_OF_COMBAT); + + return false; + } + } + } + switch (targetType) { // single target spells @@ -1734,6 +1780,24 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_PetMaster: + { + + Mob *owner = nullptr; + + if (IsPet()) + owner = GetOwner(); + else if ((IsNPC() && CastToNPC()->GetSwarmOwner())) + owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); + + if (!owner) + return false; + + spell_target = owner; + CastAction = SingleTarget; + break; + } + default: { mlog(SPELLS__CASTING_ERR, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); @@ -1833,7 +1897,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } // check line of sight to target if it's a detrimental spell - if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional) + if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional) { mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName()); Message_StringID(13,CANT_SEE_TARGET); @@ -1863,12 +1927,21 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 //casting a spell on somebody but ourself, make sure they are in range float dist2 = DistNoRoot(*spell_target); float range2 = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; if(dist2 > range2) { //target is out of range. mlog(SPELLS__CASTING, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); Message_StringID(13, TARGET_OUT_OF_RANGE); return(false); } + else if (dist2 < min_range2){ + //target is too close range. + mlog(SPELLS__CASTING, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2); + Message_StringID(13, TARGET_TOO_CLOSE); + return(false); + } + + spell_target->CalcSpellPowerDistanceMod(spell_id, dist2); } // @@ -2052,7 +2125,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 std::list targets_in_range; std::list::iterator iter; - entity_list.GetTargetsForConeArea(this, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); iter = targets_in_range.begin(); while(iter != targets_in_range.end()) { @@ -2068,16 +2141,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || (heading_to_target >= 0.0f && heading_to_target <= angle_end)) { - if(CheckLosFN(spell_target)) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + } } } else { if(heading_to_target >= angle_start && heading_to_target <= angle_end) { - if(CheckLosFN((*iter))) + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + } } } ++iter; @@ -4625,12 +4702,10 @@ void Mob::Stun(int duration) if(stunned && stunned_timer.GetRemainingTime() > uint32(duration)) return; - if(casting_spell_id) { - int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; - if(IsClient()) - persistent_casting += aabonuses.PersistantCasting; + if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) { + int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting + aabonuses.PersistantCasting; - if(MakeRandomInt(1,99) > persistent_casting) + if(MakeRandomInt(0,99) > persistent_casting) InterruptSpell(); } 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; }; /*